2015-05-11 12 views
8

Nie jestem pewien, w jaki sposób mogę być pewien równości/niezmienności interfejsu funkcjonalnego. Sądzę, że nie ma sposobu, aby zapewnić równość, gdy używam tego cukru syntaktycznego w języku Java 8, proszę dać mi znać, jeśli masz jakieś.Równość wystąpienia interfejsu funkcjonalnego w java

Zrobiłem krótki fragment kodu dla mojego pytania.

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

i starałem się dodać instancję tego interfejsu w sposób funkcjonalny

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

} 

drukuje prawda co oznacza, że ​​nie było żadnych sprawdzenie równości i również nie mogę usunąć instancję z zestawu po dodaniu go.

jeśli używam interfejsu funkcjonalnego jako argumentu, czy jest jakiś sposób, aby jakoś porównać te instancje?

docenią każdą pomoc, z góry dzięki!

+1

zmienność jest określana przez państwo. Gdy obiekt nie ma stanu (tj. Gdy nie ma pól instancji), nie można go zmienić. Obiekty bezpaństwowe, takie jak większość (wszystkie?) Lambdy, są zatem zawsze niezmienne. Obiekty trwałe mogą i powinny być pojedynczymi, ponieważ ich stan nigdy się nie zmienia, ale możliwe jest posiadanie dwóch identycznych niemodyfikowalnych obiektów na różnych adresach pamięci ... co dałoby dwa równoważne obiekty o odrębnej tożsamości. – scottb

+2

Krótka odpowiedź: Nie, nie możesz przetestować dwóch lambd z różnych lokalizacji kodu dla równości. –

+2

@Louis Wasserman: lokalizacja kodu nie ma znaczenia, prawidłowa odpowiedź jest taka, że ​​nie można przetestować dwóch lambd dla równości, nawet jeśli utworzono instancję w tym samym miejscu kodu. Chociaż może się wydawać, że działa w niektórych przypadkach, jest to zachowanie zależne od implementacji. – Holger

Odpowiedz

3

można przechowywać Referencyjna metoda do zmiennej:

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

ten sposób zwraca false.

Podczas tworzenia tego samego labmda lub odniesienie metody w różnych miejscach kodu, to jest mniej więcej taka sama, jak można utworzyć nową klasę anonimową w obu pozycjach:

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

Więc za każdym razem jest to inny przedmiot, choć może wyglądać tak samo. Dla każdego napotkanego odniesienia metoda oddzielne klasy anonimowy jest tworzony w czasie wykonywania:

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

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

Wyjście będzie tak:

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

Praktycznie więc to dwa odrębne obiekty dwóch różnych klas. Byłoby dość trudno stwierdzić, że robią to samo, nie porównując swojego kodu bajtowego. Zauważ również, że oba przechwytują zmienną functionSet, więc należy również upewnić się, że nie została zmieniona między dwoma odwołaniami do metod.

Jedyne obejście mogę wymyślić jest zadeklarować wszystkie odniesienia metody jako stałe w kodzie, a potem odwoływać się do nich bezpośrednio, zamiast przy użyciu metody odniesienia:

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

"Za każdym razem inny obiekt" ... Niezbyt ładne, ale w porządku, chyba. Ale dlaczego nie można "równy się" działać poprawnie? – Thilo

+1

Domyślną implementacją 'equals()' jest metoda dziedziczona z Object, która jest taka sama jak operator '=='. Jeśli masz dwa oddzielne wystąpienia tego samego niezmiennego obiektu, 'equals()' będzie fałszywe, ponieważ każdy ma swoją własną odrębną tożsamość. Jeśli ponownie używasz lambda, powinieneś przechowywać ją w polu 'static' i uzyskać do niej dostęp za pośrednictwem nazwy pola, jak zasugerował Tagir. Zapobiegnie to wielu instancjom. – scottb

+1

@Thilo: Dodano więcej wyjaśnień i możliwe obejście problemu, chociaż nie jestem pewien, czy to cię uszczęśliwi. –

Powiązane problemy