Przystępujemy do następnego artykułu po zapowiedzianej dłuższej przerwie. W nawiązaniu do klas wewnętrznych, teraz przystąpimy do bardziej nowoczesnego sformułowania. Ono istnieje od powstania Javy 8 więc o nim też mogliście już nieraz usłyszeć. Przedstawiam Państwu wyrażenie lambda w języku Java (ang. "lambda expression")! Pokażę na czym ono polega i dlaczego opłaca się go poznać.
Tweet |
WYRAŻENIE LAMBDA W JĘZYKU JAVA. WYJAŚNIENIA NA POCZĄTEK
Wyrażenie to określane jest również jako "funkcja anonimowa", choć zdarzają się zróżnicowania pomiędzy jednym, a drugim. Konstrukcja jest esencją paradygmatu funkcyjnego i pozwala na dwie istotne rzeczy:
- definiowanie krótkich funkcji z możliwością pominięcia pewnych części składni, takich jak słowo kluczowe "return" czy klamerki zdając się na "intuicję" Javy
- osadzanie do innych funkcji samego KODU jako parametru dzięki czemu mamy realny wpływ na zmianę zachowania tej samej metody przy każdorazowym wywołaniu
W punkcie pierwszym chodzi o możliwość zdefiniowania instrukcji pozbywając się jednocześnie elementów, które mogą odwracać uwagę od istoty rzeczy. Nie piszemy typu zwracanej wartości, nie podajemy nazwy, nic z tych rzeczy! Zostało to tak sprytnie zbudowane, aby po prostu móc "wrzucić" instrukcje do wykonania na kształt "jednorazówki". Napisać, wykonać, wyrzucić. Co nie znaczy, że nie można podstawić czegoś takiego do zmiennej, aby korzystać do woli:
Runnable runnable = () -> System.out.println("Działa!");
Punkt drugi to droga do wstawienia jako parametr do funkcji...kodu! Zapewne część z Was korzystała z takich metod jak "addActionListener" czy innych o podobnie brzmiącej nazwie. No to co wtedy mogliście pisać? Na przykład coś takiego:
addActionListener(e -> [instrukcja]);
addActionListener(e -> {
[instrukcjaA]
[instrukcjaB]
});
Wówczas osadzaliście lambdę do parametru, czyli kod jaki ma się wykonać w wyniku działania zdarzenia (ang. "event"). Dlatego macie prawo decydować o tym CO ma się wykonać.
WIDZĘ COŚ DZIWNEGO...
Wyrażenie lambda w języku Java, oprócz "dziwnej" budowy funkcji, jest bardzo łatwo rozpoznawalne przez pewien nietypowy operator w postaci znaków myślnika i zamknięcia nawiasu trójkątnego postawionych obok siebie bez spacji (->). Jest to nazywane "strzałką". Taki sam operator znajdziecie w języku C podczas odwoływania się do zmiennych struktury czy unii poprzez wskaźnik. Ważne by sobie zakodować, że jak widzimy ten znaczek, to mamy do czynienia z wyrażeniem lambda.
Słowo "lambda" może kojarzyć się ekspertom od matematyki z rachunkiem lambda służącym do badania zagadnień dotyczących m.in. rekurencji i wyrażeń logicznych, ale na tym się kończy cała zbieżność. To nie ma nic wspólnego z jakimikolwiek obliczeniami. "Lambda" to również jedenasta litera greckiego alfabetu, a że język grecki zawsze "trzymał się" matmy, nikogo z nas to nie powinno dziwić.
WYRAŻENIE LAMBDA W PRAKTYCE
Wbrew temu, co można sobie pomyśleć o funkcji która może występować lokalnie tylko dla jednego przypadku, wyrażenie lambda czyni programowanie naprawdę o wiele prostszym. Przede wszystkim, dla wszelkiego rodzaju kolekcji, tablic, list, drzew, grafów, czy jeszcze innych konstrukcji przechowujących wiele elementów. Powtórzę. O WIELE prostszym. Najczęściej spotykana postać to para nawiasów okrągłych (wśród nich mogą być parametry, a w przypadku jednego, nawiasy można wtedy zupełnie pominąć), operator "strzałki" i instrukcja. Tu jest ciekawie bo mamy dwa sposoby definicji.
- jedna instrukcja
- możemy pominąć klamerki i tuż po tym operatorze podać jedną instrukcję, na przykład "System.out.println".
- możemy pominąć klamerki i tuż po tym operatorze podać jedną instrukcję, na przykład "System.out.println".
- więcej instrukcji
- podajemy klamerki tworząc blok kodu do wykonania, a w nim wprowadzamy szereg instrukcji.
A teraz rozwinę myśli.
JEDNA INSTRUKCJA
Wyrażenie lambda ma to do siebie, że potrafi skutecznie skrócić kod do granic wytrzymania. Gdybyśmy mieli utworzyć własną metodę wykonującą jedną instrukcję, musimy wtedy rozpisać się tak:
void printSquare(int n) {
System.out.println("Kwadrat liczby " + n + " wynosi " + n*n);
}
i oprócz jednej konkretnej instrukcji mamy parę linijek "szumu". A po skorzystaniu z lambdy:
LambdaWithNumber lwn = n -> System.out.println("Kwadrat liczby " + n + " wynosi " + n*n);
skupiamy się na konkrecie jakim jest wypisanie kwadratu podanej liczby. Gdybyście chcieli zrobić taki manewr, potrzebny Wam będzie interfejs z pojedynczą metodą:
@FunctionalInterface
public interface LambdaWithNumber {
void printN(int n);
}
Mamy do czynienia z interfejsem funkcyjnym, który charakteryzuje się posiadaniem tylko jednej metody. Potem wystarczy wywołać tę metodę tak samo, jak to już robiliście i podać parametr liczbowy:
lwn.printN(50);
Dużo częściej będziecie korzystać z takiej formy podczas "podpinania" instrukcji do zdarzeń, tak jak choćby "addActionListener" wymienione u góry albo metoda "forEach". Jednak gdyby narastała potrzeba niestandardowej definicji, streściłem Wam co i jak wygląda.
WIELE INSTRUKCJI
Lambda może wykonywać wiele instrukcji, tylko wtedy musicie zadbać o klamerki. Tak, takie same jak przy zwykłych metodach:
LambdaWithNumber lwn = n -> {
int square = n*n;
System.out.println("Kwadrat liczby " + n + " wynosi " + square);
};
Poza uważaniem na średniki, nic więcej nie wymaga komentarza.
Wyrażenie lambda zawdzięcza swoją nazwę greckiej literze "lambda", która jest znana w matematyce między innymi w wykorzystaniu "rachunku lambda".
Źródło: Wikimedia
To wszystko. Położyłem duży nacisk na teorię i sposoby zapisu, bo wyrażenie lambda jest bardzo potężną bronią i naprawdę opłaci się jej poznanie i zrozumienie.