Programowanie funkcyjne w Javie rozwinęło się wraz z wprowadzeniem wyrażeń lambda i interfejsów funkcyjnych (od Javy 8).
Historia pytania
Przed pojawieniem się Javy 8 wszystkie interfejsy były zbiorem abstrakcyjnych metod, a paradygmat był skupiony na OOP. Wprowadzenie interfejsów funkcyjnych i wyrażeń lambda pozwoliło pisać bardziej zwięzły kod i stosować zasady FP, co poprawiło czytelność i wyrazistość kodu.
Problem
Kod związany z obsługą zdarzeń, kolekcji lub logiką asynchroniczną stawał się nadmiarowy: trzeba było tworzyć osobne klasy lub anonimowe klasy wewnętrzne. Utrudniało to utrzymanie i skalowanie kodu.
Rozwiązanie
Interfejs funkcyjny to interfejs posiadający dokładnie jedną metodę abstrakcyjną. Można go używać jako celu dla wyrażeń lambda. W standardowej bibliotece pojawiły się typowe interfejsy funkcyjne, takie jak Function<T, R>, Predicate<T>, Supplier<T>, Consumer<T>, a także możliwość tworzenia własnych interfejsów.
Przykład kodu:
import java.util.function.Function; public class FunctionalExample { public static void main(String[] args) { // Standardowy interfejs funkcyjny Function Function<String, Integer> stringLength = s -> s.length(); System.out.println(stringLength.apply("Java")); // Wydrukuje 4 } }
Kluczowe cechy:
Czy można zadeklarować interfejs funkcyjny z wieloma metodami abstrakcyjnymi, jeśli pozostałe metody mają implementację domyślną lub są statyczne?
Nie, interfejs funkcyjny może zawierać tylko jedną metodę abstrakcyjną. Metody domyślne (default) lub statyczne można mieć.
Czy można dziedziczyć interfejs funkcyjny z innego interfejsu z wieloma metodami abstrakcyjnymi?
Nie, jeśli końcowy interfejs będzie miał więcej niż jedną metodę abstrakcyjną, przestaje być interfejsem funkcyjnym i nie można go użyć do podstawienia wyrażenia lambda.
Czym interfejs funkcyjny Javy różni się od interfejsów SAM w innych językach, na przykład w C#?
W Javie nie ma bezpośredniego słowa kluczowego do deklaracji interfejsów SAM przed pojawieniem się adnotacji @FunctionalInterface. W przeciwieństwie do C#, gdzie delegate wyraźnie definiuje sygnaturę, w Javie wystarczy jedna metoda abstrakcyjna i opcjonalna adnotacja dla kompilatora.
@FunctionalInterface — kompilator nie zgłasza błędu od razu, jeśli interfejs przestaje być funkcyjny.W dużym projekcie zdecydowano się powszechnie stosować lambdy, także tam, gdzie byty reprezentowały dane, niezwiązane bezpośrednio z logiką biznesową. W rezultacie trudno było śledzić skomplikowaną logikę, lambdy maskowały intencje kodu, a debugowanie stawało się utrudnione.
Plusy:
Minusy:
W innym projekcie starannie analizowano, które interfejsy nadają się do FP. Używano Predicate, Function i własnych interfejsów do przetwarzania kolekcji i zdarzeń. Dla bytów i przechowywania danych FP nie stosowano.
Plusy:
Minusy: