Twitter: @grzegg
Kategoria: java, Tagi: - - - - .

Java [36] – Klasy anonimowe i wyrażenia lambda

Z klasami anonimowymi spotkaliśmy się przy okazji pierwszego starcia z JavąFX. Teraz wyjaśnię krótko czym są a następnie poznamy wyrażenia lambda, które pozwalają m. in. na przekazywanie kodu do metod.

Klasy anonimowe

Kiedy zapoznawaliśmy się z podstawami JavyFX spotkaliśmy się z takim kodem:

Kod od trzeciej linii wyglądał nieco dziwnie. Zamiast przekazać metodzie setOnAction() obiekt, pojawiło się coś co z jednej strony przypomina tworzenie obiektu (słowo kluczowe new) a z drugiej kod klasy z metodą handle(). Nie stworzyliśmy tu obiektu, który posiadałby nazwę ale użyliśmy klasy anonimowej. Taka konstrukcja jest wygodna na przykład wtedy, gdy chcemy przekazać do metody obiekt, będący instancją klasy, którą użyjemy tylko raz w kodzie. W takiej sytuacji tworzenie klasy a następnie obiektu, który następnie przekazujemy metodzie jest niepotrzebnym mnożeniem kodu. Od razu deklarujemy klasę i tworzymy jej instancje (obiekt). W tym wypadku tworzymy klasę implementującą interfejs EventHandler.

Nie będę szerzej opisywać klas anonimowych, choć ich używanie jest wygodne w wielu sytuacjach, ponieważ począwszy od Javy 8 udostępniono jeszcze wygodniejsze wyrażenia lambda

Wyrażenia lambda

Wyrażenia lambda są w pewien sposób podobne do anonimowych klas, ponieważ można je określić jako anonimowe metody. Z tego wynika, że będą odpowiadały pojedynczym metodom, które można na przykład przekazać jako argument do innej metody. Rzeczywiście, wyrażenie lambda implementuje metodę zdefiniowaną w interfejsie funkcjonalnym. Czym zatem jest interfejs funkcjonalny? Jest to taki interfejs, który posiada tylko jedną metodę abstrakcyjną (niezaimplementowaną), może natomiast posiadać wiele metod domyślnych. Jeśli nie rozumiesz w czym rzecz, warto zajrzeć do lekcji poświęconej interfejsom. Przy tworzeniu takich interfejsów dobrze jest wyraźnie zaznaczyć, że są one funkcjonalne używając adnotacji @FunctionalInterface.

Generalnie struktura wyrażenia lambda wygląda tak:


(parametr1, parametr2,...) -> {
ciało lambdy
}

Przy czym możliwe są różne modyfikacje tego wzorca. Na przykład może nie być wcale parametrów:


() -> {
ciało lambdy
}

Jeśli ciało lambdy zawiera tylko jedną linię kodu, nawiasy klamrowe są zbędne:


(parametr1) -> polecenie;

Jeśli jest tylko jeden parametr, można ominąć nawiasy:


parametr -> polecenie;

Spróbujmy teraz stworzyć interfejs funkcjonalny a następnie użyć wyrażenia lambda.

Interfejs Thinkable

Klasa WyrazeniaLambda

Zwróć uwagę, że w powyższych przykładach nie określaliśmy typu parametru, choć jest to możliwe:


pomyslCos((String st) -> { System.out.println("Dlatego "+ st);},
"używaj wyrażeń lambda");

Rodzaj argumentu został „wywnioskowany” z kodu interfejsu.

Predefinowane interfejsy funkcjonalne

Co ciekawe w wielu wypadkach wcale nie musimy tworzyć interfejsu aby używać wyrażeń lambda. Java dostarcza wielu funkcjonalnych interfejsów do których można dopasować wyrażenia lambda uwzględniając rodzaj argumentów i zwracanych wartości zadeklarowanych w nich metod abstrakcyjnych. Można ich szukać na przykład w dokumentacji pakietu java.util.function ale także w innych miejscach. Na przykład interfejs Runnable należy do podstawowego pakietu java.lang.

Wróćmy teraz do pakietu java.util.function. Można tam znaleźć na przykład interfejs Consumer, który, jak wynika z dokumentacji, deklaruje abstrakcyjną metodę accept(Object) i nie zwraca żadnej wartości. Biorąc pod uwagę, że łańcuch znaków też jest obiektem (typu String) to może zupełnie niepotrzebnie utworzyliśmy w poprzednim przykładzie interfejs?

Utwórz nowy program, który będzie bardzo podobny do poprzedniego ale zamiast interfejsu Thinkable użyjemy zaimportowanego Consumer i oczywiście nazwę wywoływanej metody należy zmienić z mysl() na accept():

Zauważ, że poza oszczędniejszym pisaniem kodu, zaletą wyrażeń lambda jest łatwe przekazywanie fragmentów kodu do metod. Pozwala to tworzyć metody, które są bardzo elastyczne nie tylko pod względem wartości przyjmowanych argumentów ale także kodu, który wykonują. W powyższym przykładzie zaimplementowaliśmy na przykład różne sposoby wypisywania tekstu przekazywanego metodzie, ale przecież można przekazać w ten sposób dużo bardziej złożony kod. W poniższym przykładzie metoda funkcja robi bardzo różne rzeczy z różnymi obiektami.

Na koniec…

Wyrażenia lambda to kolejny dość szeroki temat, który zaledwie musnęliśmy. Na razie ważne jest, żebyś rozumiał ten rodzaj kodu a także potrafił go wykorzystywać przynajmniej w stopniu podstawowym, ponieważ przyda się w dalszych częściach kursu. Można je bowiem efektywnie wykorzystać m. in. przy tworzeniu interfejsu graficznego w JavieFX.

Przypomnij sobie kod, z od którego rozpoczęliśmy lekcję:

Teraz zmieńmy go używając wyrażenia lambda:

Czyż nie jest krótszy i bardziej przejrzysty?

Leave a Reply