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

Interfejs funkcyjny

Idea interfejsu funkcyjnego 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, że 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
}

Wyrażenie lambda

Celem stworzenia takie rodzaju interfejsu, jest możliwość skorzystania z tzw. wyrażeń lambda (ang. lambda expression). Są one próba zaimplementowania koncepcji związanych z programowaniem funkcyjnych* w pełni obiektowych 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();
	}
}

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");
};

Lambdy 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 lambd

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

Może Ci się również spodoba

Dodaj komentarz

Twój adres email nie zostanie opublikowany. Pola, których wypełnienie jest wymagane, są oznaczone symbolem *