Szkolenia programowania we Wrocławiu
Kurs Java Podstawy - rozszerzony

Interfejs funkcyjny

  1. Czym jest interfejs funkcyjny
  2. Interfejsy funkcyjne w API Javy
  3. Przykład

Czym jest interfejs funkcyjny

Interfejs funkcyjny jest interfejsem, który posiada tylko jedną metodę abstrakcyjną. Pozwala on w Javie (od wersji 8) na wykorzystanie wyrażeń lambda w miejsce, gdzie normalnie kompilator oczekuje takiego typu danych. Interfejsy funkcyjne opcjonalnie oznaczane są adnotacją @FunctionalInterface.

Ósma wersja języka Java wprowadziła ogromną nowość w postaci możliwości wykorzystania funkcyjnego paradygmatu programowania. Dzięki temu kod w wielu miejscach może być znacznie uproszczony i bardziej zwięzły. Najpopularniejszym przykładem może być możliwość wykorzystania wyrażeń funkcyjnych w miejsce gdzie wcześniej stosowane były anonimowe klasy wewnętrzne.

Przed Javą 8:

Button button = new Button("Click Me");
button.setOnAction(new EventHandler<ActionEvent>() {
	@Override
	public void handle(ActionEvent event) {
		System.out.println("Button clicked");
	}
});

Java 8:

Button button = new Button("Click Me");
button.setOnAction(event -> System.out.println("Button clicked"));

W powyższym przykładzie EventHandler jest interfejsem z Javy FX. Posiada on tylko jedną metodę abstrakcyjną handle() więc oznacza to, że jest interfejsem funkcyjnym. Metoda setOnAction() oczekuje argumentu typu EventHandler, a więc w to miejsce możemy przekazać wyrażenie lambda event -> System.out.println("Button clicked").

Interfejsy funkcyjne w API Javy 8

W Javie 8 wiele klas i interfejsów zostało zaktualizowanych, aby stać się interfejsami funkcyjnymi oraz wspierać operacje z ich wykorzystaniem. Podstawowe "funkcje" możliwe są do wykorzystania przy wykorzystania typów zdefiniowanych w pakiecie java.util.function. Oprócz tego interfejsami funkcyjnymi są np.:

  • Comparator
  • EventHandler (JavaFX)
  • ActionListener (Swing)
  • Runnable, Callable

Przykład

Definiujemy interfejs funkcyjny, który będzie definiował metodę przyjmującą wartość typu String i nie zwracającą żadnego wyniku (void).

package pl.javastart;

@FunctionalInterface
public interface StringConsumer {
	
	void consumeString(String str);
	
}

Teraz zobaczmy przykład jego wykorzystania w metodzie:

public static void printList(List<String> list, StringConsumer consumer) {
	for(String str: list) {
		consumer.consumeString(str);
	}
}

Metoda printList() (statyczność nie ma tutaj znaczenia) przyjmuje listę i StringConsumera. W metodzie nie jest powiedziane co StringConsumer robi, jedynie aplikujemy mu jako argument kolejne elementy listy. Cała magia polega na tym, że przy wywołaniu metody printList() możemy teraz przekazać wyrażenie lambda w miejsce StringConsumera.

package pl.javastart;

import java.util.Arrays;
import java.util.List;

public class StringConsumerExample {
	public static void main(String[] args) {
		List<String> names = Arrays.asList("Kasia", "Ania", "Zosia", "Bartek");
		printList(names, str -> System.out.println(str));
	}

	public static void printList(List<String> list, StringConsumer consumer) {
		for (String str : list) {
			consumer.consumeString(str);
		}
	}
}

W ten sposób wywołanie metody

consumer.consumeString(str);

możemy utożsamiać z :

System.out.println(str);

Idąc dalej, możemy zauważyć, że w pakiecie java.util.function już tak naprawdę istnieje interfejs podobny do naszego, który nazywa się Consumer i nasz przykład można uprościć i nie definiować własnego interfejsu.

package pl.javastart;

import java.util.Arrays;
import java.util.List;
import java.util.function.Consumer;

public class StringConsumerExample {
	public static void main(String[] args) {
		List<String> names = Arrays.asList("Kasia", "Ania", "Zosia", "Bartek");
		printList(names, str -> System.out.println(str));
	}

	public static void printList(List<String> list, Consumer<String> consumer) {
		for (String str : list) {
			consumer.accept(str);
		}
	}
}

interfejs funkcyjny wynik

Przykład na Github

Komentarze