2015-04-15 19 views
17

Jestem całkiem nowy dla java i staram się po prostu zrozumieć, jak interpretować @Override metod equals() i hashcode().
wiem na metodzie równa się być poprawne musi być:Która część umowy ogólnej equals() nie spełnia moich oczekiwań

  1. refleksyjne:a.equals(a)
  2. symetryczny:a.equals(b)następnieb.equals(a)
  3. przechodnie:a.equals(b) && b.equals(c)Następniea.equals(c)
  4. NOT NULL:! a.equals(null)

walczę, aby wskazać, które z powyższych właściwości jestem i jestem niespełniających pisząc mój overide metody równymi.

Jestem świadomy, że zaćmienie może je wygenerować dla mnie, jednak ponieważ nie mam jeszcze pełnego pojęcia, pisanie go pomaga mi się uczyć.

Napisałem, co uważam za poprawny sposób, ale kiedy sprawdzam z wersją generowaną przez eclipse, wydaje mi się, że "brakuje" pewnych aspektów.

Przykład:

public class People { 

    private Name first; //Invariants --> !Null, !=last 
    private Name last; // !Null, !=first 
    private int age; // !Null, ! <=0 
    ... 
} 

Co napisałem:

public boolean equals(Object obj){ 
    if (obj == null){ 
     return false; 
    } 
    if (!(obj instanceof People)){ 
     return false; 
    } 
    People other = (People) obj; 
    if (this.age != other.age){ 
     return false; 
    } 
    if (! this.first.equals(other.first)){ 
     return false; 
    } 
    if (! this.last.equals(other.last)){ 
     return false; 
    } 
    return true; 
} 

vs zaćmienie generowane

public boolean equals(Object obj) { 
    if (this == obj) 
     return true; 
    if (obj == null) 
     return false; 
    if (getClass() != obj.getClass()) 
     return false; 

    People other = (People) obj; 
    if (first == null) { 
     if (other.first != null) 
      return false; 
    } else if (!first.equals(other.first)) 
     return false; 
    if (age != other.age) 
     return false; 
    if (last == null) { 
     if (other.last != null) 
      return false; 
    } else if (!last.equals(other.last)) 
     return false; 

    return true; 
} 

mi brakuje:

  • if (this == obj) 
        return true; 
    
  • if (getClass() != obj.getClass()) 
        return false; 
    
  • I dla każdej zmiennej:

    if (first == null) { 
        if (other.first != null) 
         return false; 
    } else if (!first.equals(other.first)) 
        return false; 
    

Nie jestem pewien, co getClass() jest i moja implmentation błędna?

+0

Dlaczego używasz nazwy "Name" w implementacji równań dla "People"? Wygląda jak literówka. Proszę o wyjaśnienie. – Radiodef

+5

Możesz także zobaczyć [* "Jakiś powód, aby preferować getClass() nad instanceof podczas generowania .equals()?" *] (Http://stackoverflow.com/q/596462/2891664) Cała odpowiedź może być napisana właśnie na tylko ten temat. – Radiodef

+1

Nadal masz, jeśli (!(obj instanceof Name)) {line wrong –

Odpowiedz

8

Pierwszy kawałek kodu:

if (this == obj) 
    return true; 

To poprawia wydajność w przypadku, gdy porównamy odniesienie do obiektu przeciwko sobie. Przykład: a.equals(a);.

Drugi kawałek kodu:

if (getClass() != obj.getClass()) 
    return false; 

porównania jeśli klasa odniesienia są porównywane jest taka sama klasa this. Różnica między stosowaniem tego podejścia i instanceof polega na tym, że jest on bardziej restrykcyjny w porównaniu z klasą podrzędną. Przykład:

public class Foo { } 
public class Bar extends Foo { } 

//... 
Foo foo = new Bar(); 
System.out.println(foo instanceof Bar); //prints true 
System.out.println(foo instanceof Foo); //prints true 
Foo foo2 = new Foo(); 
System.out.println(foo.getClass() == foo2.getClass()); //prints false 

Którą wybrać? Nie ma dobrego ani złego podejścia, zależy to od pożądanego projektu.

Trzeci kawałek kodu:

if (first == null) { 
     if (other.first != null) 
      return false; 
    } else if (!first.equals(other.first)) 
     return false; //For each variable. 

To jest po prostu zerowa wyboru dla każdego pola odniesienia obiektu w klasie. Zauważ, że jeśli this.first jest null, to wykonanie this.first.equals(...) spowoduje wyświetlenie NullPointerException.

+2

Jeśli zastanawiasz się, dlaczego ostatni fragment nie używa 'Objects.equals' ... ta część Eclipse została prawdopodobnie napisana przed dodaniem' Objects.equals' (w Javie 7). – immibis

4

Nie sądzę implementacja jest niepoprawna, ale kilka uwag:

if (this == obj) 
     return true; 

jest to optymalizacja wydajności, to bezpośrednio testuje dla równości odniesienia i zwarciami testów gdzie ajesta.

if (getClass() != obj.getClass()) 
     return false; 

jest podobna do rozmowy instanceof, optymalizuje dala null czek. Pozostałe połączenia wydają się być zerowymi kontrolami.

+4

Podobne, ale nie to samo. "instanceof" jest prawdziwe dla podklasy, która może złamać regułę "symetryczną". – pkalinow

3

Nie trzeba pisać

if (obj == null){ 
     return false; 
} 
if (!(obj instanceof People)){ 
    return false; 
} 

ponieważ null zawsze daje false w instanceof kontrole. Więc te linie mogą zostać uproszczone, aby po prostu

if (!(obj instanceof People)){ 
    return false; 
} 

Co do głównego pytania, czy wybrana metoda spełnia wymagania dla metody equals(), ściśle mówiąc odpowiedź brzmi nie, albo przynajmniej jest to potencjalnie ryzykowna. To dlatego, że byłoby to możliwe, aby rozszerzyć klasę następująco

public class SpecialPeople extends People { 

    // code omitted 

    @Override 
    public boolean equals(Object object) { 
     if (object == null || object.getClass() != getClass()) 
      return false; 
     SpecialPeople other = (SpecialPeople) object; 
     return other.getAge() == getAge() 
       && other.getFirst().equals(getFirst()) 
       && other.getLast().equals(getLast()); 
} 

Teraz załóżmy a jest instancją People i b jest instancją SpecialPeople. Załóżmy także, że a i b mają tę samą nazwę i wiek. Następnie

a.equals(b) == true // instanceof check succeeds 
b.equals(a) == false // getClass() check fails 
Dlatego equals() nie jest symetryczny! Z tego powodu, jeśli używasz instanceof zamiast getClass() w equals() prawdopodobnie powinieneś albo wykonać equals() metodę final lub klasę final.

+0

W przypadku korzystania z obiektu obj.getClass należy najpierw sprawdzić wartość zerową. Zależy od podejścia, które podejmiesz w sprawie getClass lub instanceOf –

+0

@ Noman_1 Najpierw sprawdziłem 'null'. –