Pelo que eu sei, existem apenas dois tipos de funções, destrutivas e construtivas.
Enquanto a função construtiva, como o nome indica, constrói algo, uma função destrutiva destrói algo, mas não da maneira que você pode pensar agora.
Por exemplo, a função
Function<Integer,Integer> f = (x,y) -> x + y
é construtivo . Como você precisa construir algo. No exemplo, você construiu a tupla (x, y) . As funções construtivas têm o problema de não serem capazes de lidar com argumentos infinitos. Mas o pior é que você não pode simplesmente deixar uma discussão aberta. Você não pode simplesmente dizer "bem, deixe x: = 1" e experimentar todos os y que desejar. Você tem que construir a cada vez toda a tupla com
x := 1
. Então, se você quiser ver o que as funções retornam, y := 1, y := 2, y := 3
você precisa escrever f(1,1) , f(1,2) , f(1,3)
.
No Java 8, as funções construtivas devem ser tratadas (na maioria das vezes) usando referências de método porque não há muita vantagem em usar uma função lambda construtiva. Eles são um pouco como métodos estáticos. Você pode usá-los, mas eles não têm estado real.
O outro tipo é o destrutivo, pega algo e desmonta na medida do necessário. Por exemplo, a função destrutiva
Function<Integer, Function<Integer, Integer>> g = x -> (y -> x + y)
faz o mesmo que a função f
que foi construtiva. Os benefícios de uma função destrutiva são: agora você pode lidar com infinitos argumentos, o que é especialmente conveniente para fluxos, e pode simplesmente deixar os argumentos abertos. Portanto, se você quiser ver novamente como seria o resultado se x := 1
e y := 1 , y := 2 , y := 3
, você pode dizer h = g(1)
e
h(1)
é o resultado para y := 1
, h(2)
para y := 2
e h(3)
para y := 3
.
Então aqui você tem um estado fixo! Isso é bastante dinâmico e na maioria das vezes é o que queremos de um lambda.
Padrões como o Factory são muito mais fáceis se você puder apenas incluir uma função que faça o trabalho para você.
Os destrutivos são facilmente combinados uns com os outros. Se o tipo estiver certo, você pode apenas compor como quiser. Usando isso, você pode definir facilmente morfismos que tornam (com valores imutáveis) o teste muito mais fácil!
Você também pode fazer isso com uma composição construtiva, mas a composição destrutiva parece mais bonita e mais parecida com uma lista ou um decorador, e a construtiva se parece muito com uma árvore. E coisas como voltar atrás com funções construtivas simplesmente não são legais. Você pode apenas salvar as funções parciais de uma destrutiva (programação dinâmica), e no "retrocesso" apenas usar a função destrutiva antiga. Isso torna o código muito menor e mais legível. Com funções construtivas, você tem mais ou menos para se lembrar de todos os argumentos, o que pode ser muito.
Então, por que é necessário BiFunction
haver mais perguntas do que por que não há TriFunction
?
Primeiro de tudo, muito tempo você tem apenas alguns valores (menos de 3) e precisa apenas de um resultado, então uma função destrutiva normal não seria necessária, uma função construtiva seria suficiente. E há coisas como mônadas que realmente precisam de uma função construtiva. Mas, fora isso, não há realmente muitas boas razões para haver um BiFunction
. O que não significa que deva ser removido! Eu luto por minhas Mônadas até morrer!
Portanto, se você tiver muitos argumentos, que não podem ser combinados em uma classe de contêiner lógica, e se precisar que a função seja construtiva, use uma referência de método. Caso contrário, tente usar a nova habilidade adquirida de funções destrutivas, você pode se ver fazendo muitas coisas com muito menos linhas de código.