Per quanto ne so, ci sono solo due tipi di funzioni, distruttive e costruttive.
Mentre la funzione costruttiva, come suggerisce il nome, costruisce qualcosa, una distruttiva distrugge qualcosa, ma non nel modo in cui potresti pensare ora.
Ad esempio, la funzione
Function<Integer,Integer> f = (x,y) -> x + y
è costruttivo . Poiché hai bisogno di costruire qualcosa. Nell'esempio hai costruito la tupla (x, y) . Le funzioni costruttive hanno il problema di non essere in grado di gestire infiniti argomenti. Ma la cosa peggiore è che non puoi lasciare una discussione aperta. Non puoi semplicemente dire "beh, lascia x: = 1" e provare ogni y che potresti voler provare. Devi costruire ogni volta l'intera tupla con
x := 1
. Quindi, se ti piace vedere cosa restituiscono le funzioni, y := 1, y := 2, y := 3
devi scrivere f(1,1) , f(1,2) , f(1,3)
.
In Java 8, le funzioni costruttive dovrebbero essere gestite (la maggior parte delle volte) utilizzando riferimenti a metodi perché non c'è molto vantaggio nell'usare una funzione lambda costruttiva. Sono un po 'come i metodi statici. Puoi usarli, ma non hanno uno stato reale.
L'altro tipo è quello distruttivo, prende qualcosa e lo smantella per quanto necessario. Ad esempio, la funzione distruttiva
Function<Integer, Function<Integer, Integer>> g = x -> (y -> x + y)
fa lo stesso della funzione f
che era costruttiva. I vantaggi di una funzione distruttiva sono che ora puoi gestire infiniti argomenti, il che è particolarmente conveniente per i flussi, e puoi semplicemente lasciare gli argomenti aperti. Quindi, se vuoi di nuovo vedere come sarebbe il risultato se x := 1
e y := 1 , y := 2 , y := 3
, puoi dire h = g(1)
ed
h(1)
è il risultato per y := 1
, h(2)
per y := 2
e h(3)
per y := 3
.
Quindi qui hai uno stato fisso! È abbastanza dinamico e la maggior parte delle volte è quello che vogliamo da un lambda.
I pattern come Factory sono molto più facili se puoi semplicemente inserire una funzione che fa il lavoro per te.
Quelli distruttivi si combinano facilmente tra loro. Se il tipo è giusto puoi semplicemente comporli come preferisci. Usandolo, puoi facilmente definire morfismi che rendono (con valori immutabili) i test molto più facili!
Puoi farlo anche con uno costruttivo, ma la composizione distruttiva sembra più carina e più simile a un elenco o un decoratore, e quella costruttiva assomiglia molto a un albero. E cose come il backtracking con funzioni costruttive non sono piacevoli. Puoi semplicemente salvare le funzioni parziali di una distruttiva (programmazione dinamica), e su "backtrack" basta usare la vecchia funzione distruttiva. Ciò rende il codice molto più piccolo e meglio leggibile. Con le funzioni costruttive devi più o meno ricordare tutti gli argomenti, il che può essere molto.
Allora perché c'è bisogno di BiFunction
dovrebbe essere più interrogativo che perché non ce n'è TriFunction
?
Prima di tutto, molto tempo hai solo pochi valori (meno di 3) e hai bisogno solo di un risultato, quindi una normale funzione distruttiva non sarebbe affatto necessaria, una costruttiva andrebbe bene. E ci sono cose come le monadi che hanno davvero bisogno di una funzione costruttiva. Ma a parte questo, non ci sono davvero molte buone ragioni per cui esiste BiFunction
. Il che non significa che debba essere rimosso! Combatto per le mie Monadi finché non muoio!
Quindi, se hai molti argomenti, che non puoi combinare in una classe contenitore logica, e se hai bisogno che la funzione sia costruttiva, usa un riferimento al metodo. Altrimenti prova a usare la nuova abilità acquisita delle funzioni distruttive, potresti trovarti a fare molte cose con molte meno righe di codice.