O ile wiem, istnieją tylko dwa rodzaje funkcji, destrukcyjne i konstruktywne.
Podczas gdy funkcja konstruktywna, jak sama nazwa wskazuje, coś konstruuje, to destrukcyjna coś niszczy, ale nie w taki sposób, w jaki myślisz teraz.
Na przykład function
Function<Integer,Integer> f = (x,y) -> x + y
jest konstruktywna . Ponieważ musisz coś skonstruować. W tym przykładzie utworzyłeś krotkę (x, y) . Funkcje konstruktywne mają problem polegający na tym, że nie są w stanie obsłużyć nieskończonych argumentów. Ale najgorsze jest to, że nie możesz po prostu pozostawić otwartej kłótni. Nie możesz po prostu powiedzieć „no cóż, niech x: = 1” i wypróbować wszystkie y, które chcesz spróbować. Za każdym razem musisz utworzyć całą krotkę z
x := 1
. Więc jeśli chcesz zobaczyć, co zwracają funkcje y := 1, y := 2, y := 3
, musisz napisać f(1,1) , f(1,2) , f(1,3)
.
W Javie 8 funkcje konstruktywne powinny być obsługiwane (przez większość czasu) przy użyciu odwołań do metod, ponieważ nie ma zbytniej korzyści z używania konstruktywnej funkcji lambda. Są trochę jak metody statyczne. Możesz ich używać, ale nie mają rzeczywistego stanu.
Drugi typ jest destrukcyjny, zabiera coś i demontuje w razie potrzeby. Na przykład funkcja destrukcyjna
Function<Integer, Function<Integer, Integer>> g = x -> (y -> x + y)
robi to samo, co funkcja, f
która była konstruktywna. Zaletą funkcji destrukcyjnej jest to, że możesz teraz obsługiwać nieskończone argumenty, co jest szczególnie wygodne w przypadku strumieni, i możesz po prostu pozostawić argumenty otwarte. Więc jeśli znowu chcesz zobaczyć, jaki byłby wynik, jeśli x := 1
i y := 1 , y := 2 , y := 3
, możesz powiedzieć h = g(1)
i
h(1)
jest wynikiem dla y := 1
, h(2)
dla y := 2
i h(3)
dla y := 3
.
Więc tutaj masz ustalony stan! To dość dynamiczne i przez większość czasu to jest to, czego oczekujemy od lambdy.
Wzorce takie jak Factory są o wiele łatwiejsze, jeśli możesz po prostu wstawić funkcję, która wykonuje pracę za Ciebie.
Niszczycielskie można łatwo łączyć ze sobą. Jeśli typ jest odpowiedni, możesz po prostu skomponować je według własnego uznania. Używając tego, możesz łatwo zdefiniować morfizmy, które znacznie ułatwiają testowanie (przy niezmiennych wartościach)!
Możesz to też zrobić konstruktywną, ale destrukcyjna kompozycja wygląda ładniej i bardziej przypomina listę lub dekorator, a konstruktywna bardzo przypomina drzewo. A rzeczy takie jak wycofywanie się z funkcjami konstruktywnymi są po prostu nieprzyjemne. Możesz po prostu zapisać częściowe funkcje destrukcyjnej (programowanie dynamiczne), a na "backtrack" po prostu użyć starej funkcji destrukcyjnej. To sprawia, że kod jest dużo mniejszy i bardziej czytelny. Dzięki funkcjom konstruktywnym masz mniej więcej do zapamiętania wszystkich argumentów, których może być dużo.
Dlaczego więc istnieje potrzeba, aby BiFunction
być bardziej kwestionowanym, niż dlaczego nie ma TriFunction
?
Po pierwsze, przez wiele czasu masz po prostu kilka wartości (mniej niż 3) i potrzebujesz tylko wyniku, więc normalna funkcja niszcząca nie byłaby w ogóle potrzebna, konstruktywna byłaby w porządku. Są rzeczy takie jak monady, które naprawdę potrzebują konstruktywnej funkcji. Ale poza tym nie ma zbyt wielu dobrych powodów, dla których w ogóle istnieje BiFunction
. Co nie oznacza, że należy go usunąć! Walczę o moje monady, aż umrę!
Więc jeśli masz wiele argumentów, których nie możesz połączyć w logiczną klasę kontenera, i jeśli chcesz, aby funkcja była konstruktywna, użyj odwołania do metody. W przeciwnym razie spróbuj użyć nowo zdobytej zdolności funkcji niszczących, może się okazać, że robisz wiele rzeczy z dużo mniejszą liczbą linii kodu.