Site icon Java blog

Java 8 #2: wyrażenia lambda i interfejs funkcyjny

lambda expression

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

Exit mobile version