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);
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);
}
}
}
Przykład na Github
Dyskusja i komentarze
Masz pytania do tego wpisu? Może chcesz się podzielić spostrzeżeniami? Zapraszamy dyskusji na naszej grupie na Facebooku.