2012-09-02 11 views
10

Kiedy czytam książkę Java, autor powiedział, że podczas projektowania klasy zazwyczaj nie jest bezpieczne korzystanie z dziedziczenia przez equals(). Na przykład:Dlaczego nie powinienem używać równań z dziedziczeniem?

public final class Date { 
    public boolean equals(Object o) { 
     // some code here 
    } 
} 

w klasie powyżej, powinniśmy umieścić final, więc inna klasa nie może dziedziczyć z tego. Moje pytanie brzmi: dlaczego nie jest to bezpieczne, gdy pozwala na to inna klasa?

+0

Opcja "Comparable" określa "compareTo", a nie 'equals', przy okazji. – oldrinb

+0

Och. dziękuję :) Edytowałem: D – hqt

+0

Jak nazywa się ta książka? –

Odpowiedz

19

Ponieważ jest to trudne (niemożliwe?), Aby to naprawić, zwłaszcza symmetric property.

Powiedz, że masz klasę Vehicle i klasę Car extends Vehicle. Vehicle.equals() plony true, jeśli argument jest również Vehicle i ma taką samą wagę. Jeśli chcesz wdrożyć Car.equals() powinno uzyskując true tylko jeśli argument jest także samochód, a oprócz wagi, należy również dokonać porównania, silnik, itp

teraz wyobrazić następujący kod:

Vehicle tank = new Vehicle(); 
Vehicle bus = new Car(); 
tank.equals(bus); //can be true 
bus.equals(tank); //false 

Pierwsze porównanie może dać true, jeśli przez koincydencję zbiornik i autobus mają tę samą wagę. Ale ponieważ czołg nie jest samochodem, porównanie go z samochodem zawsze przyniesie false.

Masz kilka obejścia:

  • ścisłe: dwa obiekty są równe wtedy i tylko wtedy mają dokładnie ten sam typ (i wszystkie właściwości są równe). To jest złe, np. gdy podklasujesz ledwo, aby dodać jakieś zachowanie lub udekorować oryginalną klasę. Niektóre frameworki podklasują również klasy bez zauważania (Hibernate, Spring AOP z proxy CGLIB ...)

  • loose: dwa obiekty są równe, jeśli ich typy są "zgodne" i mają tę samą zawartość (semantycznie). Na przykład. dwa zestawy są równe, jeśli zawierają te same elementy, nie ma znaczenia, że ​​jeden jest HashSet, a drugi to TreeSet (dzięki @veer za wskazanie tego).

    To może być mylące. Weź dwa numery: LinkedHashSet s (gdzie zamówienie reklamowe jest ważne w ramach umowy). Jednak ponieważ equals() zajmuje tylko surowe Set kontraktu uwzględnieniu rentowności porównania true nawet dla oczywiście różnych obiektów:

    Set<Integer> s1 = new LinkedHashSet<Integer>(Arrays.asList(1, 2, 3)); 
    Set<Integer> s2 = new LinkedHashSet<Integer>(Arrays.asList(3, 2, 1)); 
    System.out.println(s1.equals(s2)); 
    

Zobacz także

+1

Nie * niemożliwe *; patrz np. ['Set.equals'] (http://docs.oracle.com/javase/7/docs/api/java/util/Set.html#equals (java.lang.Object)) – oldrinb

+0

@veer: Zaktualizowałem moje odpowiedź, dzięki. –

+0

@TomaszNurkiewicz jako link podaj mi, jeśli użyjemy 'if (this.getClass()! = Tank.getClass()) {return false;}' lub 'if (this.getClass()! = Tank.getClass()) {return false;} ', problem zostanie rozwiązany. Czy możesz powiedzieć mi więcej o tej sprawie? – hqt

8

Martin Odersky (the facet za generics w Javie i oryginalny code dla bieżącego javac) ma ładny rozdział w swojej książce, adresując ten problem. Sugeruje, że dodanie metody canEqual może rozwiązać problem równości/dziedziczenia.Można zapoznać się z dyskusją w pierwszym wydaniu swojej książki, która jest dostępna w Internecie:

Chapter 28 of Programming in Scala, First Edition: Object Equality

Książka jest oczywiście odnosząc się do Scala, ale te same idee mają zastosowanie do klasycznej Java. Przykładowy kod źródłowy nie powinien być zbyt trudny do zrozumienia dla osoby pochodzącej z tła Java.

Edit:

Wygląda Odersky opublikował artykuł na temat samego pojęcia w języku Java z powrotem w 2009 roku, i jest ona dostępna na tej samej stronie internetowej:

How to Write an Equality Method in Java

Naprawdę nie myśl, że próbując podsumować artykuł w tej odpowiedzi, zrobi to sprawiedliwie. Obejmuje on temat równości obiektów w gruncie, od typowych błędów wprowadzanych w implementacjach równości do pełnej dyskusji na temat Java equals jako relacji równoważności. Powinieneś po prostu to przeczytać.