Eu tenho uma pergunta sobre o uso do Function.identity()
método.
Imagine o seguinte código:
Arrays.asList("a", "b", "c")
.stream()
.map(Function.identity()) // <- This,
.map(str -> str) // <- is the same as this.
.collect(Collectors.toMap(
Function.identity(), // <-- And this,
str -> str)); // <-- is the same as this.
Existe alguma razão para você usar em Function.identity()
vez de str->str
(ou vice-versa). Eu acho que a segunda opção é mais legível (uma questão de gosto, é claro). Mas, existe alguma razão "real" pela qual alguém deve ser preferido?
java
lambda
java-8
java-stream
Przemysław Głębocki
fonte
fonte
t -> t
simplesmente porque é mais sucinto.Respostas:
Na implementação atual do JRE,
Function.identity()
sempre retornará a mesma instância, enquanto cada ocorrência deidentifier -> identifier
não apenas criará sua própria instância, mas também terá uma classe de implementação distinta. Para mais detalhes, veja aqui .O motivo é que o compilador gera um método sintético que mantém o corpo trivial dessa expressão lambda (no caso de
x->x
, equivalente areturn identifier;
) e informa ao tempo de execução para criar uma implementação da interface funcional que chama esse método. Portanto, o tempo de execução vê apenas métodos de destino diferentes e a implementação atual não analisa os métodos para descobrir se certos métodos são equivalentes.Portanto, usar em
Function.identity()
vez dex -> x
pode economizar um pouco de memória, mas isso não deve conduzir sua decisão se você realmente acha quex -> x
é mais legível do queFunction.identity()
.Você também pode considerar que, ao compilar com as informações de depuração ativadas, o método sintético terá um atributo de depuração de linha apontando para a (s) linha (s) de código-fonte que contém a expressão lambda, portanto, você poderá encontrar a origem de uma
Function
instância específica durante a depuração . Por outro lado, ao encontrar a instância retornadaFunction.identity()
durante a depuração de uma operação, você não saberá quem chamou esse método e passou a instância para a operação.fonte
x -> x
quadro. Você sugere definir o ponto de interrupção para este lambda? Normalmente não é tão fácil de colocar o ponto de interrupção no lambda-expressão única (pelo menos em Eclipse) ...Function.identity()
essa informação, é perdida. Em seguida, a cadeia de chamada pode ajudar em casos simples de pensar, a avaliação por exemplo multi-thread onde o iniciador original não está no rastreamento de pilha ...new
.new Foo(…)
garantias para criar uma nova instância do tipo exatoFoo
, enquanto que,Foo.getInstance(…)
pode retornar uma instância existente de (um subtipo de)Foo
…No seu exemplo, não há grande diferença entre
str -> str
eFunction.identity()
uma vez que internamente é simplest->t
.Mas às vezes não podemos usar
Function.identity
porque não podemos usar aFunction
. Dê uma olhada aqui:isso irá compilar bem
mas se você tentar compilar
você receberá um erro de compilação, pois
mapToInt
esperaToIntFunction
, o que não está relacionadoFunction
. TambémToIntFunction
não temidentity()
método.fonte
i -> i
porFunction.identity()
resultará em um erro do compilador.mapToInt(Integer::intValue)
.mapToInt(i -> i)
é a simplificação demapToInt( (Integer i) -> i.intValue())
. Use a versão que você achar mais clara, pois para mimmapToInt(i -> i)
mostra melhor as intenções desse código.i -> i
parece uma função de identidade, que não é neste caso.i -> i
pois meu objetivo é mapear Integer para int (o quemapToInt
sugere bastante bem) para não chamar explicitamente ointValue()
método. Como esse mapeamento será alcançado não é realmente tão importante. Então, vamos apenas concordar em discordar, mas obrigado por apontar uma possível diferença de desempenho, precisarei dar uma olhada mais de perto nisso algum dia.Da fonte JDK :
Portanto, não, desde que esteja sintaticamente correto.
fonte
t->t
no código-fonte pode criar um objeto e a implementação deFunction.identity()
é uma ocorrência. Portanto, todos os sites que chamamidentity()
compartilharão esse objeto, enquanto todos os sites que usarem explicitamente a expressão lambdat->t
criarão seu próprio objeto. O métodoFunction.identity()
não é especial de forma alguma, sempre que você cria um método de fábrica que encapsula uma expressão lambda comumente usada e chama esse método em vez de repetir a expressão lambda, você pode economizar memória, dada a implementação atual .t->t
objeto cada vez que o método é chamado e recicla o mesmo sempre que o método é chamado?invokedynamic
instrução que é vinculada em sua primeira execução executando o chamado método de autoinicialização, que no caso de expressões lambda está localizado noLambdaMetafactory
. Essa implementação decide retornar um identificador para um construtor, um método de fábrica ou código sempre retornando o mesmo objeto. Ele também pode decidir retornar um link para um identificador já existente (o que atualmente não acontece).