2015-05-11 15 views
8

Non sono sicuro di come sia possibile garantire l'uguaglianza/immutabilità dell'interfaccia funzionale. Immagino che non ci sia modo di assicurare l'uguaglianza quando uso questo zucchero sintattico in java 8, per favore fammi sapere qualsiasi suggerimento se ne hai.Uguaglianza di istanza dell'interfaccia funzionale in java

Ho creato un breve snippet di codice per la mia domanda.

public interface Element { 
    void doSomething(int a); 
} 

e ho cercato di aggiungere un'istanza di questa interfaccia in modo funzionale

public class FunctionSet { 

    public void doubleUp(int a) { 
     System.out.println(a*2); 
    } 

    public void square(int a) { 
     System.out.println(a*a); 
    } 

    public static void main(String[] args) { 
     HashSet<Element> set = new HashSet<>(); 
     FunctionSet functionSet = new FunctionSet(); 

     set.add(functionSet::doubleUp); 
     set.add(functionSet::square); 

     System.out.println(set.add(functionSet::doubleUp)); 
    } 

} 

la stampa vera che significa che non ci fosse alcun controllo di uguaglianza e anche io non può rimuovere qualsiasi istanza da Set una volta che lo aggiungo.

nel caso in cui utilizzo l'interfaccia funzionale come argomento, esiste un modo per confrontare in qualche modo tali istanze?

apprezzeranno qualsiasi aiuto, grazie in anticipo!

+1

Mutabilità è determinata dallo stato. Quando un oggetto non ha uno stato (cioè, quando non ha campi di istanza), non può essere mutabile. Gli oggetti apolidi, come la maggior parte (tutti?) Lambda sono quindi sempre immutabili. Gli oggetti immutabili possono e devono essere singoletti poiché il loro stato non può mai cambiare, ma è possibile avere due oggetti immutabili altrimenti identici a indirizzi di memoria diversi ... che darebbero due oggetti equivalenti con un'identità separata. – scottb

+2

Risposta breve: No, non è possibile testare due lambda da percorsi di codice diversi per l'uguaglianza. –

+2

@Louis Wasserman: la posizione del codice non ha importanza, la risposta corretta è che non è possibile testare due lambda per l'uguaglianza, anche se istanziati nello stesso percorso di codice. Mentre potrebbe sembrare che funzioni in alcuni casi, questo è un comportamento dipendente dall'implementazione. – Holger

risposta

3

È possibile memorizzare il vostro riferimento metodo in una variabile:

public static void main(String[] args) { 
    HashSet<Element> set = new HashSet<>(); 
    FunctionSet functionSet = new FunctionSet(); 

    Element fn = functionSet::doubleUp; 
    set.add(fn); 
    set.add(functionSet::square); 

    System.out.println(set.add(fn)); 
} 

In questo modo restituisce false.

Quando si crea lo stesso labmda o metodo di riferimento in diversi punti del codice, è più o meno lo stesso che è necessario creare una nuova classe anonima in entrambe le posizioni:

public static void main(String[] args) { 
    HashSet<Element> set = new HashSet<>(); 
    FunctionSet functionSet = new FunctionSet(); 

    set.add(new Element() { 
     @Override 
     public void doSomething(int a) { 
      functionSet.doubleUp(a); 
     } 
    }); 
    set.add(new Element() { 
     @Override 
     public void doSomething(int a) { 
      functionSet.square(a); 
     } 
    }); 

    System.out.println(set.add(new Element() { 
     @Override 
     public void doSomething(int a) { 
      functionSet.doubleUp(a); 
     } 
    })); 
} 

Così ogni volta che si tratta di un oggetto diverso, anche se potrebbe sembrare lo stesso. Per ogni riferimento metodo incontrato classe anonima viene creata in fase di esecuzione:

Element e1 = functionSet::doubleUp; 
Element e2 = functionSet::doubleUp; 

System.out.println(e1.getClass()); 
System.out.println(e2.getClass()); 

L'output sarà simile a questo:

class FunctionSet$$Lambda$1/918221580 
class FunctionSet$$Lambda$2/1554547125 

Quindi in pratica si tratta di due oggetti distinti di due classi distinte. Sarebbe abbastanza difficile concludere che facciano la stessa cosa senza paragonare il loro bytecode. Si noti inoltre che entrambi catturano la variabile functionSet, pertanto è necessario assicurarsi che non sia stata modificata tra due riferimenti al metodo.

L'unica soluzione che posso pensare a è quello di dichiarare tutti i riferimenti di metodo come costanti nel codice e poi li di riferimento invece di utilizzare direttamente il metodo riferimenti:

public static final Element FN_DOUBLE_UP = new FunctionSet()::doubleUp; 
public static final Element FN_SQUARE = new FunctionSet()::square; 

public static void main(String[] args) { 
    HashSet<Element> set = new HashSet<>(); 

    set.add(FN_DOUBLE_UP); 
    set.add(FN_SQUARE); 

    System.out.println(set.add(FN_DOUBLE_UP)); 
} 
+0

"Oggetto diverso ogni volta" ... Non bello, ma va bene, immagino. Ma perché non è possibile che "equals" funzioni correttamente? – Thilo

+1

L'implementazione predefinita di 'equals()' è il metodo ereditato da Object, che è lo stesso dell'operatore '=='. Se hai due istanze separate dello stesso oggetto immutabile, 'equals()' sarà falso perché ognuna ha una sua identità separata. Se stai riutilizzando un lambda, dovresti memorizzarlo in un campo 'static' e accedervi tramite il nome del campo come Tagir ha suggerito. Ciò impedirà più istanze. – scottb

+1

@Thilo: aggiunte ulteriori spiegazioni e possibili soluzioni alternative, anche se non sono sicuro che questo ti renderà felice –