Wyrażenie lambda
Wyrażenia lambda (ang. lambda expression) są próbą zaimplementowania koncepcji związanych z programowaniem funkcyjnych* w pełni obiektowym języku jakim jest Java. Koncept ten jest bardzo stary, o czym możesz się przekonać czytając chociażby blog Martina Fowlera**. Dotychczas w Javie pewną brzydką alternatywą dla wyrażeń lambda były klasy anonimowe. Sam przyznasz jednak, że nie są one zbyt eleganckim rozwiązaniem.
Wyrażenia lambda wyglądają dużo lepiej i poprawiają czytelność kodu. Kolejną przewagą wyrażeń lambda, jest to, że obliczenia zawarte w nich nie wychodzą poza blok funkcji, co rozwiązuje wiele problemów w programowaniu obiektowym, związanych z kontrolą stanu obiektu***. Przypominają one też funkcje matematyczne typu f(x)=x+1, z tą różnicą, że w tym przypadku, masz też możliwość używania takiej funkcji nie podając żadnego argumentu i de facto nic nie zwracając.
public class LambdaTest {
public static void main(String[] args) {
Playable anonymousClass = new Playable() {
@Override
public void play() {
System.out.println("JDK 7");
}
};
Playable lambdaExpression = () -> System.out.println("JDK 8");
anonymousClass.play();
lambdaExpression.play();
}
}Interfejs funkcyjny
Aby móc skorzystać z wyrażenia lambda, należy wpierw zaimplementować z tzw. interfejs funkcyjny. Jego idea jest bardzo prosta, jest to zwykły interfejs tylko, że z jedną metodą abstrakcyjną (sygnaturą). Ważne podkreślenia jest też to, że Twój interfejs funkcyjny może mieć dowolną liczbę metod statycznych i domyślnych. Dodatkowo warto skorzystać z adnotacji @FunctionalInterface, która ostrzeże Cię przed skompilowaniem kodu, gdy Twój interfejs nie stosuje się do zasad opisanych wcześniej.
Przykładowy interfejs funkcyjny:
@FunctionalInterface
public interface Playable {
public void play();
//public void rewind(); can't add more than one signature to functional interface
}Klasa anonimowa vs. interfejs funkcyjny
W przypadku powyżej, dwukrotnie skorzystałem z interfejsu Playable, raz implementując go w formie klasy anonimowej, drugi raz jako wyrażenie lambda. Wydaje się, że obie formy różni tylko zapis. To jednak nie prawda. Postaraj się sprawdzić poniższy zapis:
Object playable = new Playable() {
@Override
public void play() {
System.out.println("JDK 7");
}
};
Object lambda = () -> {
System.out.println("JDK 8");
}; //error, lambda can't be an object, until it gets cast to specific interface
Object lambdaAfterCasting = (Playable) () -> {
System.out.println("JDK 8");
};Wyrażenia lambda i klasa Object
Dotąd mówiłem, że wszystko w Javie (poza typami prostymi) jest obiektem. Okazuje się, że od JDK w wersji 8, nie jest tak do końca. Lambda jest wyjątkiem i może być przypisana tylko i wyłącznie do konkretnego interfejsu funkcyjnego. W tej lekcji pokazałem Ci jedynie najprostszą z możliwych lambd do napisania, ale się domyślasz nie stoi nic na przeszkodzie, aby napisać bardziej skomplikowany interfejs funkcyjny.
@FunctionalInterface
public interface Drivable {
public int accelerate(int force);
}Drivable driver = x -> {
System.out.println(x);
return x*x;
};
int force = 5;
System.out.println(driver.accelerate(force));Inne właściwości wyrażenia lambda
Taka lambda nie tylko przyjmuje argument, ale także zwraca wartość. Co więcej ma więcej niż jedną linię kodu. Możesz też używać lambd, w przypadku natywnych funkcji JDK, które spełniają warunki bycie interfejsem funkcyjnych (np. w przypadku Runnable). Więcej wariacji przeróżnych interfejsów funkcyjnych opiszę Ci w następnej lekcji.
*Więcej o programowaniu funkcyjnym: https://en.wikipedia.org/wiki/Functional_programming
**Link do notki Martina Fowlera z 2004 roku (!) odnośnie wyrażeń lambda: https://martinfowler.com/bliki/Lambda.html
***Przeczytaj o tzw. side effects w programowaniu: https://www.radford.edu/nokie/classes/320/Tour/side.effects.html
Kod do lekcji: https://github.com/developeronthego/java-jdk8/tree/master/src/main/java/java8/lambda
Czym jest klasa anonimowa, znajdziesz u mnie w lekcji: Java #44: klasy wewnętrzne

