2014-12-23 16 views
22

In Java, il codice seguente restituisce false in entrambe le query. Perché? Non sarebbe più semplice per i riferimenti al metodo essere singleton? Certamente renderebbe molto più semplice attaccare e staccare gli ascoltatori. Poiché è necessario mantenere una costante per qualsiasi riferimento al metodo che dovrà essere verificato in equivalenza, non è possibile utilizzare l'operatore di riferimento del metodo in ogni posizione necessaria.Perché i riferimenti al metodo non sono singleton?

public class Main { 

    public Main() { 
     // TODO Auto-generated constructor stub 
    } 

    public void doStuff() { 

    } 

    public static void main(String[] args) { 
     Main main = new Main(); 
     Runnable thing1 = main::doStuff; 
     Runnable thing2 = main::doStuff; 
     System.out.println(thing1 == thing2); // false 
     System.out.println(thing1.equals(thing2)); // false 
    } 

} 

risposta

16

Per esempio metodi, non credo che avrebbe senso per loro di essere memorizzati nella cache. Dovresti memorizzare nella cache un metodo per istanza ... il che significherebbe un campo aggiuntivo all'interno della classe associata al metodo: uno per metodo pubblico, presumibilmente, perché i metodi potrebbero essere referenziati da fuori della classe o memorizzati nella cache utente del riferimento al metodo, nel qual caso sarà necessaria una sorta di cache per istanza.

Penso che sia più sensato per i riferimenti al metodo metodi statici da memorizzare nella cache, perché saranno sempre gli stessi. Tuttavia, per memorizzare nella cache l'effettivo Runnable, avresti bisogno di una cache per tipo a cui puntava. Per esempio:

public interface NotRunnable { 
    void foo(); 
} 

Runnable thing1 = Main::doStuff; // After making doStuff static 
NotRunnable thing2 = Main::doStuff; 

caso thing1 e thing2 essere uguali qui? In che modo il compilatore saprebbe che tipo di creare, se sì? Potrebbe creare qui due istanze e memorizzarle in una cache separata - e sempre cache nel punto di utilizzo piuttosto che nel punto della dichiarazione del metodo. (Il vostro esempio ha la stessa classe di dichiarare il metodo e il riferimento a esso, che è un caso molto speciale. Si dovrebbe prendere in considerazione il caso più generale in cui sono diversi>)

Il JLS permette per il metodo di riferimenti a essere memorizzati nella cache . Da section 15.13.3:

successivo, sia una nuova istanza di una classe con le proprietà di seguito viene allocata e inizializzata, o un'istanza esistente di una classe con le proprietà di seguito viene fatto riferimento.

... ma anche per i metodi statici, sembra che javac non faccia alcun caching al momento.

+0

Quindi, perché non eseguire l'override di .equals() per i metodi di riferimento? Tutto ciò che sarebbe richiesto sarebbe ognuno con un riferimento a 'main' e' Main.class.getMethod ("doStuff") '(Grazie per il suggerimento @CostiCiudatu) – Dimitriye98

+0

@ Dimitriye98: Questa è sicuramente una possibilità - ma tu dovresti è necessario definire con precisione la semantica. Ad esempio, i riferimenti a metodi di istanza su due oggetti distinti ma uguali sono uguali? Forse ... ma ogni volta che ti sei affidato a 'equals' come questo ti baseresti su un dettaglio di implementazione della funzione - cosa succederebbe se qualcuno implementasse' Runnable' manualmente invece? –

+0

Sembrerebbe che il mio edit sia stato ninja perché ho premuto per sbaglio invio invece di shift-enter e ho inviato il commento in anticipo: P – Dimitriye98

4

In questo semplice caso, è possibile eseguire come suggerito creando campi statici o di istanze aggiuntivi come appropriato, più complesso se il lambda fa riferimento agli oggetti, tuttavia l'intento è quello di rendere invisibili tali istanze, ad es.

List<String> list = .... 
list.stream().filter(s -> !s.isEmpty()).forEach(System.out::println); 

dovrebbe essere il più efficiente (anche creare altri oggetti diversi)

for (String s : list) 
    if(!s.isEmpty()) 
     System.out.println(s); 

Può eliminare gli oggetti inlining il codice corrente e utilizzando l'analisi di fuga per eliminare la necessità di creare oggetti nella prima posto.

Per questo motivo si è prestata poca attenzione all'implementazione di equals(), hashCode() o toString(), accedendovi tramite riflessione per le chiusure. AFAIK, questo deliberato per evitare che gli oggetti vengano utilizzati in modi non previsti.

+0

La durata della compilazione è inlinea? Se è così non dovrebbe essere un problema verificare se il riferimento al metodo lambda/sarà assegnato a qualcosa. – Dimitriye98

+0

Il problema non è se è ssigned a qualsiasi cosa, ma se "fugge" il metodo cioè se no può essere ottimizzato via e nozionalmente aggiunto allo stack invece dell'heap, a condizione che non si utilizzino metodi come uguali, ecc. –

+0

In entrambi i casi, non è solo un lavoro extra da fare per il compilatore? Non dovrebbe cambiare le prestazioni molto assumendo che il compilatore possa riconoscere correttamente quando inline e quando no. – Dimitriye98

2

Per eseguire i singleton dei metodi è necessario sincronizzare l'acquisizione di un riferimento ad essi. Questo dà un grande overhead per un'operazione così semplice con risultati imprevedibili.L'altra soluzione consiste nel creare oggetti per ogni metodo sul caricamento della classe, ma ciò comporta molti oggetti ridondanti, poiché solo pochi metodi richiedono il riferimento. Penso che la sincronizzazione sia il problema principale.