Existe algum benefício de desempenho em usar a sintaxe de referência do método em vez da sintaxe lambda no Java 8?

51

As referências a métodos ignoram a sobrecarga do wrapper lambda? Poderiam eles no futuro?

De acordo com o Java Tutorial sobre referências a métodos :

Sometimes... a lambda expression does nothing but call an existing method. In those cases, it's often clearer to refer to the existing method by name. Method references enable you to do this; they are compact, easy-to-read lambda expressions for methods that already have a name.

Eu prefiro a sintaxe lambda à sintaxe de referência do método por vários motivos:

Lambdas são mais claros

Apesar das afirmações da Oracle, acho a sintaxe lambda mais fácil de ler do que a referência de método de objeto, pois a sintaxe de referência do 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 ser uma das duas. Ele esconde o que seu código está realmente fazendo.

Lambdas são mais seguros

Se você fizer referência a 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 envolver qualquer função em um lambda - assim você pode usar a mesma sintaxe de forma consistente em todos os lugares. A sintaxe de referência do método não funcionará em métodos que obtêm 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ê tem métodos sobrecarregados com o mesmo número de argumentos, mas você não deve fazer isso de qualquer maneira (veja o item 41 de Josh Bloch), então não podemos manter isso contra referências de métodos.

Conclusão

Se não houver penalidade de desempenho por fazê-lo, sou tentado a desativar o aviso no meu IDE e usar a sintaxe lambda consistentemente sem aplicar a referência de método ocasional no meu código.

P.S.

Nem aqui nem lá, mas nos meus sonhos, as referências a métodos de objeto são mais parecidas com isso e aplicam-se a dinâmica de invocação contra o método diretamente no objeto sem um wrapper lambda:

_.foo()
    
por GlenPeterson 26.03.2015 / 16:16
fonte

3 respostas

11

Em muitas cenas, acho que lambda e method-reference são equivalentes. Mas o lambda irá envolver o alvo de invocação pelo tipo de interface declarante.

Por exemplo

public class InvokeTest {

    private static void invoke(final Runnable r) {
        r.run();
    }

    private static void target() {
        new Exception().printStackTrace();
    }

    @Test
    public void lambda() throws Exception {
        invoke(() -> target());
    }

    @Test
    public void methodReference() throws Exception {
        invoke(InvokeTest::target);
    }
}

Você verá o console gerando o stacktrace.

Em lambda() , o método que chama target() é lambda$lambda$0(InvokeTest.java:20) , que contém 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 chamador do método lambda é algo como InvokeTest$$Lambda$2/1617791695.run(Unknown Source) , que é a chamada invokedynamic na JVM, isso significa que a chamada está vinculada ao método gerado .

Em methodReference() , o método que chama target() é diretamente o InvokeTest$$Lambda$1/758529971.run(Unknown Source) , isso significa que a ligação está vinculada diretamente ao método InvokeTest::target .

Conclusão

Acima de tudo, compare a referência de método, usando a expressão lambda só irá causar um mais chamada de método para o método de geração de lambda.

    
por 11.11.2015 / 11:54
fonte
23

É tudo sobre a meta-fábrica

Primeiro, as referências do método mais não precisam de dessachamento pelo metafactory lambda, elas são simplesmente usadas como método de referência. Sob a seção "Lambda body sugaring" da Tradução das Expressões Lambda Artigo ("TLE"):

All things being equal, private methods are preferable to nonprivate, static methods preferable to instance methods, it is best if lambda bodies are desugared into in the innermost class in which the lambda expression appears, signatures should match the body signature of the lambda, extra arguments should be prepended on the front of the argument list for captured values, and would not desugar method references at all. However, there are exception cases where we may have to deviate from this baseline strategy.

Isto é ainda mais destacado no "The Lambda Metafactory" da TLE:

metaFactory(MethodHandles.Lookup caller, // provided by VM
            String invokedName,          // provided by VM
            MethodType invokedType,      // provided by VM
            MethodHandle descriptor,     // lambda descriptor
            MethodHandle impl)           // lambda body

The impl argument identifies the lambda method, either a desugared lambda body or the method named in a method reference.

Um método de instância estática ( Integer::sum ) ou ilimitada ( Integer::intValue ) é o mais "simples" ou o mais "conveniente", no sentido de que eles podem ser manipulados de maneira ideal por um "caminho rápido" variante metafactory sem a desugaring . Esta vantagem é utilmente apontada nas "variantes de meta-fábrica" do TLE:

By eliminating arguments where they are not needed, classfiles become smaller. And the fast path option lowers the bar for the VM to intrinsify the lambda conversion operation, enabling it to be treated as a "boxing" operation and faciliating unbox optimizations.

Naturalmente, uma referência de método de captura de instância ( obj::myMethod ) precisa fornecer a instância limitada como um argumento para o manipulador de método para invocação, o que pode significar a necessidade de desugaring usando métodos 'bridge'.

Conclusão

Eu não sei exatamente qual é o 'wrapper' do lambda que você está insinuando, mas mesmo que o resultado final de usar suas referências a lambdas ou métodos definidos pelo usuário seja o mesmo, o caminho o que é alcançado 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 a métodos possam ser tratadas de uma maneira mais ideal pela metafa- rativa.

    
por 29.05.2015 / 20:47
fonte
0

Há uma conseqüência bastante séria ao usar expressões lambda que podem afetar o desempenho.

Quando você declara uma expressão lambda, você está criando um fechamento sobre o escopo local.

O que isso significa e como isso afeta o desempenho?

Bem, estou feliz que você tenha perguntado. Isso significa que cada uma dessas pequenas expressões lambda é uma pequena classe interna anônima e isso significa que ela contém uma referência a todas as variáveis que estão no mesmo escopo da expressão lambda.

Isso também significa this de referência da instância do objeto e todos os seus campos. Dependendo do momento da invocação real do lambda, isso pode significar um vazamento de recursos bastante significativo, já que o coletor de lixo não pode deixar de ir a essas referências, desde que o objeto mantido em um lambda ainda esteja ativo ...

    
por 12.11.2015 / 22:55
fonte