Hasta donde yo sé, solo hay dos tipos de funciones, destructivas y constructivas.
Mientras que la función constructiva, como su nombre lo indica, construye algo, una destructiva destruye algo, pero no de la forma en que piensas ahora.
Por ejemplo, la función
Function<Integer,Integer> f = (x,y) -> x + y
es constructivo . Como necesitas construir algo. En el ejemplo, construiste la tupla (x, y) . Las funciones constructivas tienen el problema de no poder manejar argumentos infinitos. Pero lo peor es que no puedes dejar una discusión abierta. No puedes simplemente decir "bueno, deja x: = 1" y probar cada y que quieras probar. Tienes que construir cada vez toda la tupla con
x := 1
. Entonces, si desea ver qué devuelven las funciones y := 1, y := 2, y := 3
, debe escribir f(1,1) , f(1,2) , f(1,3)
.
En Java 8, las funciones constructivas deben manejarse (la mayoría de las veces) mediante el uso de referencias de métodos porque no hay mucha ventaja de usar una función lambda constructiva. Son un poco como métodos estáticos. Puede usarlos, pero no tienen un estado real.
El otro tipo es el destructivo, toma algo y lo desmantela tanto como sea necesario. Por ejemplo, la función destructiva
Function<Integer, Function<Integer, Integer>> g = x -> (y -> x + y)
hace lo mismo que la función f
que fue constructiva. Los beneficios de una función destructiva son que ahora puede manejar argumentos infinitos, lo que es especialmente conveniente para flujos, y puede dejar los argumentos abiertos. Entonces, si desea ver nuevamente cómo sería el resultado si x := 1
y y := 1 , y := 2 , y := 3
, puede decir h = g(1)
y
h(1)
es el resultado para y := 1
, h(2)
para y := 2
y h(3)
para y := 3
.
¡Así que aquí tienes un estado fijo! Eso es bastante dinámico y eso es la mayoría de las veces lo que queremos de una lambda.
Patrones como Factory son mucho más fáciles si puede poner una función que haga el trabajo por usted.
Los destructivos se combinan fácilmente entre sí. Si el tipo es correcto, puede componerlos como desee. ¡Con eso, puede definir fácilmente morfismos que hacen que las pruebas (con valores inmutables) sean mucho más fáciles!
También puedes hacer eso con una constructiva, pero la composición destructiva se ve mejor y más como una lista o un decorador, y la constructiva se parece mucho a un árbol. Y cosas como retroceder con funciones constructivas simplemente no son agradables. Puede simplemente guardar las funciones parciales de una destructiva (programación dinámica), y en "retroceder" simplemente use la antigua función destructiva. Eso hace que el código sea mucho más pequeño y mejor legible. Con funciones constructivas tienes más o menos que recordar todos los argumentos, que pueden ser muchos.
Entonces, ¿por qué es necesario BiFunction
que haya más preguntas que por qué no TriFunction
?
En primer lugar, muchas veces solo tiene unos pocos valores (menos de 3) y solo necesita un resultado, por lo que una función destructiva normal no sería necesaria en absoluto, una constructiva estaría bien. Y hay cosas como las mónadas que realmente necesitan una función constructiva. Pero aparte de eso, en realidad no hay muchas buenas razones por las que existe un BiFunction
. ¡Lo que no significa que deba eliminarse! ¡Lucho por mis Mónadas hasta que muera!
Entonces, si tiene muchos argumentos, que no puede combinar en una clase de contenedor lógico, y si necesita que la función sea constructiva, use una referencia de método. De lo contrario, intente utilizar la nueva capacidad adquirida de funciones destructivas, puede encontrarse haciendo muchas cosas con muchas menos líneas de código.