Wyrażenia lambda

Wyrażenia lambda były jednym z powodów przez które kiedyś postanowiłem założyć bloga. Spotkałem się z nimi po raz pierwszy, gdy w IntelliJ korzystałem z klas anonimowych, a on grzecznie zaznaczył mi, że mógłbym uprościć to wyrażenie. Zrozumiałem wtedy, że na każdym kroku możemy sobie ułatwiać życie, znając jedynie alternatywne rozwiązania. Wtedy postanowiłem, że podzielę się tym z innymi, zapraszam do lektury.

Czym są wyrażenia lambda?

Jest to właściwie krok Javy w stronę programowania funkcyjnego. Zostały one wprowadzone w Javie 8, pozwalając programiście stworzyć funkcję nie przynależącą do żadnej klasy.

SAM czy interfejs funkcyjny?

SAM (z ang. Single Abstract Method) i interfejs funkcyjny to właściwie to samo. Główny zamysł jest taki, że aby wykorzystać wyrażenia lambda musimy posiadać stworzony interfejs, który wymaga od nas implementacji tylko jednej metody.

Różnica jest taka, że wraz z Javą 8 dostaliśmy adnotację (wpis o adnotacjach) @FunctionalInterface dzięki której możemy zrzucić na kompilator odpowiedzialność związaną z pilnowaniem tego, aby nasz interfejs spełniał wymagania interfejsu funkcyjnego.

Warto jeszcze zaznaczyć to, że istnieją wyjątki od tej reguły. Interfejsy funkcyjne mogą dodatkowo wymagać od nas implementacji pól klasy Object takich jak equals, hashCode czy toString. Drugim wyjątkiem są metody domyślne, które przypominam, mogą zawierać ciało.

Budowa wyrażeń lambda

Jeżeli chodzi o budowę pozwolę sobie ją opisać na przykładach.

Warto zauważyć, że nie zawsze musimy podawać typy naszych zmiennych, nasz kompilator w większości przypadków jest w stanie domyśleć się nich na podstawie tego, że nasz interfejs i tak zawiera tylko jedną metodę abstrakcyjną (pobiera jej typy parametrów).

Po co i kiedy stosować wyrażenia lambda?

Wyrażenia lambda możemy wykorzystać zawsze, gdy naszym „adresatem” jest interfejs funkcjonalny. Dotyczy się to zarówno parametrów, wartości zwracanych (return), operatora przypisania jak i inicjalizacji tablic/rzutowania.

Używamy nich w celu zwiększenia przejrzystości naszego kodu, jak i zredukowania jego objętości. Używamy nich jako alternatywy dla klas anonimowych, z którymi się już prawdopodobnie spotkaliście, poniżej przykład.

Jak można się łatwo domyślić, w powyższym przykładzie zarówno klasa anonimowa, jak i wyrażenia lambda mają dokładnie takie samo zadanie – dostarczyć Comparator do metody sort, aby zdefiniować, w jaki sposób ma zostać posortowana nasza lista.

Dostęp lokalny

Dodatkowo zmienne lokalne, do których się odnosimy muszą być zadeklarowane jako finalne (bądź też efektywnie finalne – wartość nadana tylko raz).

Dostęp do pól i zmiennych statycznych

Możemy jednak pozwolić sobie na modyfikację oraz użycie zmiennych instancji obiektu oraz pól statycznych klasy.

Jak widać w powyższym kodzie nie tylko wykorzystaliśmy zmienną, ale dokonaliśmy jej modyfikacji w wyrażeniu lambda.

Interfejsy funkcyjne wbudowane w Javę

Jest to kolejne ciekawe narzędzie, które zostało nam dostarczone razem z Javą w wersji 8, w celu ułatwienia nam pracy.

Zostały one podzielone na 4 kategorie:
– Interfejsy funkcji (Function<T, R>) – T -> R
– Interfejsy dostawców (Supplier<T>) – () -> T
– Interfejsy konsumentów (Consumer<T>) – T -> void
– Interfejsy predykatów (Predicate<T>) – T -> boolean

Interfejsy funkcji

Przyjmują one wartości, przetwarzają je oraz zwracają wynik, niekoniecznie tego samego typu.

Jak widać tworzymy funkcję, która przyjmuje Stringa jako parametr, zamienia go na inta, zapisuje do wyniku oraz wyświetla.

Interfejsy dostawców

Definiujemy typ zwracanej wartości oraz nie dostarczamy żadnych parametrów

W tym przykładzie tworzymy dostawcę, który dostarcza nam wynik typu double. Jest to losowa liczba z zakresu <0;500)

Interfejsy konsumentów

Przyjmują parametry, przetwarzają je, wykonują akcję oraz nie zwracają żadnego rezultatu

W powyższym przykładzie wprowadzamy zmienną typu double, podnosimy ją do potęgi 4 oraz wyświetlamy wynik.

Interfejsy predykatów

Przyjmują one jeden parametr oraz zwracają wartość typu boolean.

W tym przykładzie sprawdzamy, czy ilość znaków we wprowadzonym przez nas String jest większa niż 4.

Coś więcej

pełna dokumentacja oracle – dokładna lista metod dostarczanych w java.util.function

Dotarliśmy do samego końca. W przypadku tego tematu szczególnie zachęcam do indywidualnych ćwiczeń. Znacznie ograniczy to czas potrzebny na podejmowanie decyzji w tym zakresie.

Temat jednak nie jest jeszcze do końca wyczerpany. Poruszając wyrażenia lambda warto wspomnieć o referencjach oraz strumieniach. Jest to jednak temat obszerny, dlatego chciałbym opisać to szczegółowo w następnym wpisie. Miłego wieczoru!

One Comment, RSS

  1. […] do metod (z ang. method reference) jest funkcjonalnością powiązaną z wyrażeniami lambda. Są to odwołania do istniejących już […]

Your email address will not be published. Required fields are marked *

*