Interfejs funkcyjny

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

Kurs Java

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

 

Dyskusja i komentarze

Masz pytania do tego wpisu? Może chcesz się podzieliś spostrzeżeniami? Zapraszamy dyskusji na naszej grupe na Facebooku.