As referências de método ignoram a sobrecarga do wrapper lambda? Eles podem no futuro?
De acordo com o Tutorial Java sobre referências de método :
Às vezes ... uma expressão lambda não faz nada além de chamar um método existente. Nesses casos, geralmente é mais claro fazer referência ao método existente por nome. As referências de método permitem que você faça isso; elas são expressões lambda compactas e fáceis de ler para métodos que já possuem um nome.
Prefiro a sintaxe lambda à sintaxe de referência do método por vários motivos:
Lambdas são mais claras
Apesar das alegações da Oracle, acho a sintaxe lambda mais fácil de ler do que a referência de método de objeto em abreviação, porque a sintaxe de referência de método é ambígua:
Bar::foo
Você está chamando um método estático de um argumento na classe de x e passando x?
x -> Bar.foo(x)
Ou você está chamando um método de instância de argumento zero em x?
x -> x.foo()
A sintaxe de referência do método pode substituir qualquer um. Ele oculta o que seu código está realmente fazendo.
Lambdas são mais seguras
Se você referenciar Bar :: foo como um método de classe e Bar posteriormente adicionar um método de instância com o mesmo nome (ou vice-versa), seu código não será mais compilado.
Você pode usar lambdas consistentemente
Você pode agrupar qualquer função em uma lambda - para poder usar a mesma sintaxe de maneira consistente em todos os lugares. A sintaxe de referência do método não funciona em métodos que recebem ou retornam matrizes primitivas, lançam exceções verificadas ou têm o mesmo nome de método usado como uma instância e um método estático (porque a sintaxe de referência do método é ambígua sobre qual método seria chamado) . Eles não funcionam quando você sobrecarregou métodos com o mesmo número de argumentos, mas você não deve fazer isso de qualquer maneira (consulte o item 41 de Josh Bloch), para que não possamos impedir isso de fazer referência a métodos.
Conclusão
Se não houver penalidade de desempenho por fazer isso, sou tentado a desativar o aviso no meu IDE e usar a sintaxe lambda de forma consistente, sem borrifar a referência ocasional do método no meu código.
PS
Nem aqui nem ali, mas nos meus sonhos, as referências de método de objeto se parecem mais com isso e aplicam invoke-dynamic contra o método diretamente no objeto sem um wrapper lambda:
_.foo()
.map(_.acceptValue())
: Se não me engano, isso se parece muito com a sintaxe do Scala. Talvez você esteja apenas tentando usar o idioma errado.System.out::println
aforEach()
todo o tempo ...?Respostas:
Em muitos cenários, acho que lambda e referência de método são equivalentes. Mas o lambda envolverá o destino de chamada pelo tipo de interface declarante.
Por exemplo
Você verá o console exibir o rastreamento de pilha.
Em
lambda()
, a chamada do métodotarget()
élambda$lambda$0(InvokeTest.java:20)
, que possui informações de linha rastreáveis. Obviamente, esse é o lambda que você escreve, o compilador gera um método anônimo para você. E então, o responsável pela chamada do método lambda é algo comoInvokeTest$$Lambda$2/1617791695.run(Unknown Source)
, ou seja, ainvokedynamic
chamada na JVM, significa que a chamada está vinculada ao método gerado .Em
methodReference()
, a chamada do métodotarget()
é diretamente aInvokeTest$$Lambda$1/758529971.run(Unknown Source)
, significa que a chamada está diretamente vinculada aoInvokeTest::target
método .Conclusão
Acima de tudo, compare com a referência do método, usar a expressão lambda causará apenas mais uma chamada de método para o método gerador a partir do lambda.
fonte
É tudo sobre a meta - fábrica
Primeiro, a maioria das referências de método não precisa de remoção de açúcar pela meta-fábrica lambda, elas são simplesmente usadas como método de referência. Na seção "Adição de açúcar no corpo da lambda" do artigo Tradução de expressões lambda ("TLE"):
Isso é destacado ainda mais abaixo em "The Lambda Metafactory":
As referências de
Integer::sum
método de instância estática ( ) ou ilimitada (Integer::intValue
) são as 'mais simples' ou as mais 'convenientes', no sentido de que elas podem ser tratadas de maneira ideal por uma variante metafábrica de 'caminho rápido' sem a remoção de açúcar . Essa vantagem é útil em "Variantes meta-fábricas" do TLE:Naturalmente, um método de captura de instância reference (
obj::myMethod
) precisa fornecer a instância delimitada como um argumento para o identificador de método para invocação, o que pode significar a necessidade de desaprovar usando métodos 'bridge'.Conclusão
Não sei exatamente qual é o 'wrapper' do lambda que você está insinuando, mas mesmo que o resultado final do uso de lambdas ou referências de método definidas pelo usuário seja o mesmo, a maneira alcançada parece ser bem diferente e pode ser diferente no futuro, se esse não for o caso agora. Portanto, suponho que seja mais provável que as referências de métodos possam ser tratadas de maneira mais otimizada pela metafábrica.
fonte
Há uma consequência bastante séria ao usar expressões lambda que podem afetar o desempenho.
Ao declarar uma expressão lambda, você está criando um fechamento sobre o escopo local.
O que isso significa e como isso afeta o desempenho?
Bem, eu estou feliz que você perguntou. Isso significa que cada uma dessas pequenas expressões lambda é uma pequena classe interna anônima e significa que ela carrega uma referência a todas as variáveis que estão no mesmo escopo da expressão lambda.
Isso significa também
this
referência da instância do objeto e de todos os seus campos. Dependendo do momento da chamada real do lambda, isso pode resultar em um vazamento de recurso bastante significativo, pois o coletor de lixo não pode deixar de lado essas referências enquanto o objeto que está mantendo um lambda ainda estiver vivo ...fonte
this
objeto que pode ser referenciado. Um lambda, portanto, não vaza recursos simplesmente por ter espaço para eles; ele se apega apenas aos objetos de que precisa. Por outro lado, uma classe interna anônima pode vazar recursos, mas nem sempre o faz. Veja este código para um exemplo: a.blmq.us/2mmrL6v