C'è un vantaggio in termini di prestazioni nell'usare la sintassi del riferimento al metodo invece della sintassi lambda in Java 8?

51

I riferimenti al metodo ignorano l'overhead del wrapper lambda? Potrebbero in futuro?

In base al tutorial Java sui riferimenti al metodo :

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.

Preferisco la sintassi lambda alla sintassi del metodo di riferimento per diversi motivi:

Lambdas è più chiaro

Nonostante le affermazioni di Oracle, trovo la sintassi lambda più facile da leggere rispetto al riferimento al metodo oggetto short-hand perché la sintassi del riferimento al metodo è ambigua:

Bar::foo

Stai chiamando un metodo statico a un argomento sulla classe di x e passandolo a x?

x -> Bar.foo(x)

O stai chiamando un metodo di istanza con argomento zero su x?

x -> x.foo()

La sintassi del riferimento al metodo potrebbe sostituire uno dei due. Nasconde ciò che il tuo codice sta effettivamente facendo.

Lambdas è più sicuro

Se fai riferimento a Bar :: pippo come metodo di classe e Bar aggiunge in seguito un metodo di istanza con lo stesso nome (o viceversa), il tuo codice non verrà più compilato.

Puoi usare lambdas in modo coerente

Puoi racchiudere qualsiasi funzione in una lambda, così puoi usare la stessa sintassi in modo coerente ovunque. La sintassi del riferimento al metodo non funzionerà su metodi che accettano o restituiscono matrici primitive, eseguono eccezioni controllate o hanno lo stesso nome di metodo utilizzato come un'istanza e un metodo statico (poiché la sintassi del riferimento al metodo è ambigua riguardo al metodo da chiamare) . Non funzionano quando hai sovraccaricato i metodi con lo stesso numero di argomenti, ma non dovresti farlo comunque (vedi l'articolo 41 di Josh Bloch) quindi non possiamo tenerlo contro i riferimenti al metodo.

Conclusione

Se non è prevista alcuna penalizzazione delle prestazioni, sono tentato di disattivare l'avviso nel mio IDE e utilizzare la sintassi lambda in modo coerente senza sprecare il riferimento al metodo occasionale nel mio codice.

P.S.

Né qui né là, ma nei miei sogni, i riferimenti al metodo degli oggetti sono più simili a questo e applicano invoke-dynamic al metodo direttamente sull'oggetto senza un wrapper lambda:

_.foo()
    
posta GlenPeterson 26.03.2015 - 16:16
fonte

3 risposte

11

In molte scene, penso che lambda e metodo-riferimento siano equivalenti. Ma lambda avvolgerà il target di invocazione con il tipo di interfaccia dichiarante.

Ad esempio

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);
    }
}

Vedrai la console emettere lo stacktrace.

In lambda() , il metodo che chiama target() è lambda$lambda$0(InvokeTest.java:20) , che ha informazioni sulla linea tracciabili. Obbligamente, questo è il lambda che scrivi, il compilatore genera un metodo anonimo per te. E poi, il chiamante del metodo lambda è qualcosa come InvokeTest$$Lambda$2/1617791695.run(Unknown Source) , cioè la chiamata invokedynamic in JVM, significa che la chiamata è collegata al metodo generato .

In methodReference() , il metodo che chiama target() è direttamente InvokeTest$$Lambda$1/758529971.run(Unknown Source) , significa che la chiamata è direttamente collegata al metodo InvokeTest::target .

Conclusione

Soprattutto, confronta con riferimento al metodo, l'uso dell'espressione lambda causerà solo una altra chiamata di metodo al metodo di generazione da lambda.

    
risposta data 11.11.2015 - 11:54
fonte
23

Tutto riguarda il metafactory

Primo, i riferimenti al metodo più non hanno bisogno di desugaring da parte della metafora lambda, sono semplicemente usati come metodo di riferimento. Sotto la sezione "Zuccheratura del corpo Lambda" della Traduzione di Lambda Expressions ("TLE") articolo:

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.

Questo è ulteriormente evidenziato più in basso in "The Lambda Metafactory" di 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.

Un riferimento statico ( Integer::sum ) o un metodo di istanza illimitata ( Integer::intValue ) è il più semplice o il più conveniente, nel senso che possono essere gestiti in modo ottimale da un "percorso veloce" metafactory variant senza il desugaring . Questo vantaggio è utile nelle "varianti Metafactory" di 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, un riferimento al metodo di acquisizione di istanze ( obj::myMethod ) deve fornire l'istanza limitata come argomento all'handle del metodo per l'invocazione, il che può significare la necessità di eseguire il desugaring utilizzando i metodi "bridge".

Conclusione

Non sono esattamente sicuro di cosa sia l'involucro lambda che stai suggerendo, ma anche se il risultato finale dell'uso dei tuoi lambda o dei riferimenti al metodo definiti dall'utente sono gli stessi, il modo ciò che viene raggiunto sembra essere abbastanza diverso e può essere diverso in futuro, se non è il caso ora. Quindi, suppongo sia più probabile che non che i riferimenti al metodo possano essere gestiti in modo ottimale dal metafactory.

    
risposta data 29.05.2015 - 20:47
fonte
0

C'è una conseguenza piuttosto grave quando si utilizzano espressioni lambda che possono influire sulle prestazioni.

Quando dichiari un'espressione lambda, stai creando una chiusura sull'ambito locale.

Che cosa significa e in che modo influisce sulle prestazioni?

Bene, sono contento che tu abbia chiesto. Significa che ognuna di quelle piccole espressioni lambda è una piccola classe interiore anonima e ciò significa che porta con sé un riferimento a tutte le variabili che si trovano nello stesso ambito dell'espressione lambda.

Questo significa anche il riferimento this dell'istanza dell'oggetto e tutti i suoi campi. A seconda del momento in cui viene effettuata l'invocazione effettiva della lambda, questo può essere una perdita di risorse piuttosto significativa dal momento che il garbage collector non è in grado di rilasciare questi riferimenti finché l'oggetto che mantiene una lambda è ancora vivo ...

    
risposta data 12.11.2015 - 22:55
fonte

Leggi altre domande sui tag