Soweit ich weiß, gibt es nur zwei Arten von Funktionen, destruktive und konstruktive.
Während konstruktive Funktionen, wie der Name schon sagt, etwas konstruieren, zerstört eine destruktive Funktion etwas, aber nicht so, wie Sie jetzt vielleicht denken.
Zum Beispiel die Funktion
Function<Integer,Integer> f = (x,y) -> x + y
ist konstruktiv . Da musst du etwas konstruieren. Im Beispiel haben Sie das Tupel (x, y) konstruiert . Konstruktive Funktionen haben das Problem, unendliche Argumente nicht verarbeiten zu können. Aber das Schlimmste ist, man kann nicht einfach ein Argument offen lassen. Sie können nicht einfach "gut, lassen Sie x: = 1" sagen und jedes y ausprobieren, das Sie ausprobieren möchten. Sie müssen jedes Mal das ganze Tupel mit konstruieren
x := 1
. Wenn Sie also sehen möchten, was die Funktionen für y := 1, y := 2, y := 3
Sie zurückgeben, müssen Sie schreiben f(1,1) , f(1,2) , f(1,3)
.
In Java 8 sollten konstruktive Funktionen (meistens) mithilfe von Methodenreferenzen behandelt werden, da die Verwendung einer konstruktiven Lambda-Funktion keinen großen Vorteil bietet. Sie sind ein bisschen wie statische Methoden. Sie können sie verwenden, aber sie haben keinen wirklichen Zustand.
Der andere Typ ist der zerstörerische, er nimmt etwas und zerlegt es so weit wie nötig. Zum Beispiel die destruktive Funktion
Function<Integer, Function<Integer, Integer>> g = x -> (y -> x + y)
macht das gleiche wie die Funktion, f
die konstruktiv war. Die Vorteile einer destruktiven Funktion sind, dass Sie jetzt unendlich viele Argumente verarbeiten können, was besonders für Streams praktisch ist, und dass Sie Argumente einfach offen lassen können. Wenn Sie also noch einmal sehen möchten, wie das Ergebnis wäre, wenn x := 1
und y := 1 , y := 2 , y := 3
, können Sie sagen h = g(1)
und
h(1)
ist das Ergebnis für y := 1
, h(2)
für y := 2
und h(3)
für y := 3
.
Hier haben Sie also einen festen Zustand! Das ist ziemlich dynamisch und das ist meistens das, was wir von einem Lambda wollen.
Muster wie Factory sind viel einfacher, wenn Sie nur eine Funktion eingeben können, die die Arbeit für Sie erledigt.
Zerstörerische lassen sich leicht miteinander kombinieren. Wenn der Typ stimmt, können Sie sie einfach nach Ihren Wünschen zusammenstellen. Auf diese Weise können Sie leicht Morphismen definieren, die das Testen (mit unveränderlichen Werten) erheblich erleichtern!
Sie können das auch mit einer konstruktiven Komposition tun, aber eine destruktive Komposition sieht besser aus und ähnelt eher einer Liste oder einem Dekorateur, und die konstruktive Komposition ähnelt einem Baum. Und Dinge wie Backtracking mit konstruktiven Funktionen sind einfach nicht schön. Sie können nur die Teilfunktionen einer destruktiven (dynamische Programmierung) speichern und auf "Backtrack" einfach die alte destruktive Funktion verwenden. Das macht Code viel kleiner und besser lesbar. Mit konstruktiven Funktionen müssen Sie sich mehr oder weniger an alle Argumente erinnern, was sehr viel sein kann.
Warum BiFunction
sollte es also mehr Fragen geben als warum gibt es keine TriFunction
?
Erstens haben Sie viel Zeit nur ein paar Werte (weniger als 3) und benötigen nur ein Ergebnis, sodass eine normale destruktive Funktion überhaupt nicht benötigt wird, eine konstruktive würde gut funktionieren. Und es gibt Dinge wie Monaden, die wirklich eine konstruktive Funktion brauchen. Aber abgesehen davon gibt es nicht wirklich viele gute Gründe, warum es BiFunction
überhaupt einen gibt. Was nicht heißt, dass es entfernt werden sollte! Ich kämpfe für meine Monaden, bis ich sterbe!
Wenn Sie also viele Argumente haben, die Sie nicht zu einer logischen Containerklasse kombinieren können, und wenn die Funktion konstruktiv sein soll, verwenden Sie eine Methodenreferenz. Wenn Sie andernfalls versuchen, die neu gewonnene Fähigkeit destruktiver Funktionen zu nutzen, werden Sie möglicherweise viele Dinge mit viel weniger Codezeilen tun.