Overriding equals () & hashCode () in sub classes ... considering super fields

Czy istnieje szczególna reguła dotycząca tego, jak nadrzędny equals() & hashCode() w podklasach rozważając super pola?? wiedząc, że jest wiele parametrów : pola super są prywatne / publiczne, z / bez gettera ...

Na przykład, Netbeans generated equals() & hashCode() nie będzie uwzględniał pól super ... oraz

    new HomoSapiens("M", "80", "1.80", "Cammeron", "VeryHot").equals(
    new HomoSapiens("F", "50", "1.50", "Cammeron", "VeryHot"))

Zwróci true : (

public class Hominidae {

    public String  gender;
    public String  weight;
    public String  height;

    public Hominidae(String gender, String weight, String height) {
        this.gender = gender;
        this.weight = weight;
        this.height = height;
    }
    ... 
}

public class HomoSapiens extends Hominidae {
    public String name;
    public String faceBookNickname;

    public HomoSapiens(String gender, String weight, String height, 
                       String name, String facebookId) {
        super(gender, weight, height);
        this.name = name;
        this.faceBookNickname = facebookId;
    }
    ...  
}

Jeśli chcesz zobaczyć wygenerowane przez Netbeans equals() & hashCode() :

public class Hominidae {

    ...

    @Override
    public boolean equals(Object obj) {
        if (obj == null) {
            return false;
        }
        if (getClass() != obj.getClass()) {
            return false;
        }
        final Hominidae other = (Hominidae) obj;
        if ((this.gender == null) ? (other.gender != null) : !this.gender.equals(other.gender)) {
            return false;
        }
        if ((this.weight == null) ? (other.weight != null) : !this.weight.equals(other.weight)) {
            return false;
        }
        if ((this.height == null) ? (other.height != null) : !this.height.equals(other.height)) {
            return false;
        }
        return true;
    }

    @Override
    public int hashCode() {
        int hash = 5;
        hash = 37 * hash + (this.gender != null ? this.gender.hashCode() : 0);
        hash = 37 * hash + (this.weight != null ? this.weight.hashCode() : 0);
        hash = 37 * hash + (this.height != null ? this.height.hashCode() : 0);
        return hash;
    }

}


public class HomoSapiens extends Hominidae {

    ...

    @Override
    public boolean equals(Object obj) {
        if (obj == null) {
            return false;
        }
        if (getClass() != obj.getClass()) {
            return false;
        }
        final HomoSapiens other = (HomoSapiens) obj;
        if ((this.name == null) ? (other.name != null) : !this.name.equals(other.name)) {
            return false;
        }
        if ((this.faceBookNickname == null) ? (other.faceBookNickname != null) : !this.faceBookNickname.equals(other.faceBookNickname)) {
            return false;
        }
        return true;
    }

    @Override
    public int hashCode() {
        int hash = 7;
        hash = 89 * hash + (this.name != null ? this.name.hashCode() : 0);
        hash = 89 * hash + (this.faceBookNickname != null ? this.faceBookNickname.hashCode() : 0);
        return hash;
    }
}
Author: wj., 2010-01-14

10 answers

dzieci nie powinny badać prywatnych członków swoich rodziców

Ale oczywiście , wszystkie istotne pola powinny być brane pod uwagę przy równości i hashowaniu.

Na szczęście, można łatwo spełnić obie Zasady.

Zakładając, że nie utkniesz przy użyciu equals i hashcode Wygenerowanych Przez NetBeans, możesz zmodyfikować metodę equals hominidae tak, aby używać instanceof comparison zamiast równości klas, a następnie użyć jej prosto. Coś jak to:


    @Override  
    public boolean equals(Object obj) {  
        if (obj == null) { return false; }  
        if (getClass() != obj.getClass()) { return false; }  
        if (! super.equals(obj)) return false;
        else {
           // compare subclass fields
        }

Oczywiście hashcode jest łatwy:


    @Override     
    public int hashCode() {     
        int hash = super.hashCode();
        hash = 89 * hash + (this.name != null ? this.name.hashCode() : 0);     
        hash = 89 * hash + (this.faceBookNickname != null ? this.faceBookNickname.hashCode() : 0);     
        return hash;     
    }     

Poważnie: co jest z tym, że NetBeans nie bierze pod uwagę pól klasy superclass wywołując metody klasy superclass?

 55
Author: CPerkins,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-11-13 14:58:04

Wolę używać EqualsBuilder (i HashcodeBuilder) z pakietu commons-lang aby moje metody equals() i hashcode() były łatwiejsze do odczytania.

Przykład:

public boolean equals(Object obj) {
 if (obj == null) { return false; }
 if (obj == this) { return true; }
 if (obj.getClass() != getClass()) {
   return false;
 }
 MyClass rhs = (MyClass) obj;
 return new EqualsBuilder()
             .appendSuper(super.equals(obj))
             .append(field1, rhs.field1)
             .append(field2, rhs.field2)
             .append(field3, rhs.field3)
             .isEquals();
}
 20
Author: matt b,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-01-14 19:42:57

Ogólnie rzecz biorąc, implementacja równości między podklasami jest trudna do utrzymania symetrycznego i przechodniego.

Rozważ klasę nadrzędną, która sprawdza pola x i y, A podklasa sprawdza pod kątem x, y i z.

Więc podklasa = = Superclass = = podklasa, gdzie z jest różna między pierwszą instancją podklasy a drugą, naruszając przechodnią część Umowy.

Dlatego typowa implementacja równości sprawdza getClass() != obj.getClass() zamiast natychmiast. W powyższym przykładzie, jeśli podklasa lub klasa Superclass wykona instanceof check, to złamie symetrię.

Więc wynik jest taki, że podklasa z pewnością może wziąć pod uwagę super.equals (), ale powinna również wykonać własne sprawdzenie getClass (), aby uniknąć powyższych problemów, a następnie dodatkowo sprawdzić, czy na własnych polach są równe. Byłaby to dziwna kaczka klasy, która zmieniła własne zachowanie na podstawie określonych pól superklasy, a nie tylko jeśli superklasa powróci równi.

 8
Author: Yishai,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-10-24 03:56:12

Zasady są następujące:

  • jest to refleksyjne: dla każdej innej niż null wartości referencyjnej x, x. equals (x) powinno zwracać true.
  • jest symetryczne: dla dowolnych wartości odniesienia innych niż null x i y, x. equals (y) powinno zwracać true wtedy i tylko wtedy, gdy y.equals(x) zwraca true.
  • jest to przechodnie: dla dowolnych wartości odniesienia innych niż null x, y i z, Jeśli x.equals(y) zwraca true, a y.equals(z) zwraca true, to x.equals(z) powinno zwracać true.
  • jest spójna: dla dowolnych wartości odniesienia innych niż null X i y, wielokrotne wywołania x.równania (y) konsekwentnie zwracają true lub konsekwentnie zwracają false, pod warunkiem, że żadne informacje użyte w porównaniach równań na obiektach nie zostaną zmodyfikowane.
  • dla każdej innej niż null wartości referencyjnej x, x. equals (null) powinno zwracać false.
  • jest ogólnie konieczne, aby nadpisać metodę hashCode za każdym razem, gdy ta metoda jest nadpisana, tak aby zachować ogólny kontrakt dla metody hashCode, która mówi, że równe obiekty muszą mieć równy hash kody

From Object.equals () .

Więc użyj pól potrzebnych do spełnienia reguł.

 4
Author: rodrigoap,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-08-12 23:07:29

Ponieważ dziedziczenie przerywa enkapsulację, podklasy implementujące equals() i hashCode () muszą koniecznie uwzględniać specyfikę ich superklas. Udało mi się zakodować wywołania do metod klasy nadrzędnej equals () i hashCode() z metod podklasy.

 2
Author: Steve Emmerson,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-01-14 19:48:09

Jeśli chodzi o zaakceptowaną odpowiedź @ CPerkins, nie sądzę, aby podany kod equals () działał niezawodnie, ze względu na prawdopodobieństwo, że super.metoda equals () sprawdzi również, czy klasa jest równa. Podklasa i nadklasa nie będą miały równych klas.

 2
Author: MattRing,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-05-10 16:46:56

Wygląda na to, że twoja klasa rodzica (super) nie nadpisuje równości. Jeśli tak jest, to musisz porównać pola z klasy nadrzędnej, gdy nadpisujesz tę metodę w podklasie. Zgadzam się, że korzystanie z commons EqualsBuiler jest drogą do zrobienia, ale musisz być ostrożny, aby nie złamać symetrii / transatywnych części umowy equals.

Jeśli twoja podklasa dodaje atrybuty do klasy nadrzędnej, A Klasa nadrzędna nie jest abstrakcyjna i nadpisuje wpakuję się w kłopoty. W tym scenariuszu powinieneś naprawdę spojrzeć na kompozycję obiektów zamiast na dziedziczenie.

Zdecydowanie polecam dział w Effective Java by Joshua Block na ten temat. Jest wyczerpująca i bardzo dobrze wyjaśniona.

 1
Author: BigMikeW,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-01-14 22:57:38

Cóż, HomoSapiens#hashcode wystarczy z odpowiedzią Cperkinsa.

@Override     
public int hashCode() {     
    int hash = super.hashCode();
    hash = 89 * hash + Objects.hash(name);     
    hash = 89 * hash + Objects.hash(faceBookNickname);     
    return hash;     
}

Jeśli chcesz te pola rodzica(gender, weight, height) w działaniu jednym ze sposobów jest utworzenie rzeczywistej instancji typu parent i użycie jej. Dzięki Bogu, to nie są abstrakcyjne zajęcia.

@Override
public boolean equals(Object obj) {
    if (obj == null) {
        return false;
    }
    if (getClass() != obj.getClass()) {
        return false;
    }
    final HomoSapiens other = (HomoSapiens) obj;
    if (!super.equals(new Hominidae(
        other.gender, other.weight, other.height))) {
         return false;
    }
    if (!Objects.equals(name, other.name)) return false;
    if (!Objects.equals(faceBookNickname, other.faceBookNickname))
        return false;
    return true;
}

Dodaję sposób (myślę) rozwiązać ten problem. Kluczowym punktem jest dodanie metody luźno sprawdzającej równość.

public class Parent {

    public Parent(final String name) {
        super(); this.name = name;
    }

    @Override
    public int hashCode() {
        return hash = 53 * 7 + Objects.hashCode(name);
    }

    @Override
    public boolean equals(final Object obj) {
        return equalsAs(obj) && getClass() == obj.getClass();
    }

    protected boolean equalsAs(final Object obj) {
        if (this == obj) return true;
        if (obj == null) return false;
        if (!getClass().isAssignableFrom(obj.getClass())) return false;
        final Parent other = (Parent) obj;
        if (!Objects.equals(name, other.name)) return false;
        return true;
    }

    private final String name;
}

I oto nadchodzi Child.

public class Child extends Parent {

    public Child(final String name, final int age) {
        super(name); this.age = age;
    }

    @Override
    public int hashCode() {
        return hash = 31 * super.hashCode() + age;
    }

    @Override
    public boolean equals(final Object obj) {
        return super.equals(obj);
    }

    @Override
    protected boolean equalsAs(final Object obj) {
        if (!super.equalsAs(obj)) return false;
        if (!getClass().isAssignableFrom(obj.getClass())) return false;
        final Child other = (Child) obj;
        if (age != other.age) return false;
        return true;
    }

    private final int age;
}

Testowanie...

@Test(invocationCount = 128)
public void assertReflective() {
    final String name = current().nextBoolean() ? "null" : null;
    final int age = current().nextInt();
    final Child x = new Child(name, age);
    assertTrue(x.equals(x));
    assertEquals(x.hashCode(), x.hashCode());
}

@Test(invocationCount = 128)
public void assertSymmetric() {
    final String name = current().nextBoolean() ? "null" : null;
    final int age = current().nextInt();
    final Child x = new Child(name, age);
    final Child y = new Child(name, age);
    assertTrue(x.equals(y));
    assertEquals(x.hashCode(), y.hashCode());
    assertTrue(y.equals(x));
    assertEquals(y.hashCode(), x.hashCode());
}

@Test(invocationCount = 128)
public void assertTransitive() {
    final String name = current().nextBoolean() ? "null" : null;
    final int age = current().nextInt();
    final Child x = new Child(name, age);
    final Child y = new Child(name, age);
    final Child z = new Child(name, age);
    assertTrue(x.equals(y));
    assertEquals(x.hashCode(), y.hashCode());
    assertTrue(y.equals(z));
    assertEquals(y.hashCode(), z.hashCode());
    assertTrue(x.equals(z));
    assertEquals(x.hashCode(), z.hashCode());
}
 1
Author: Jin Kwon,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-11-11 09:22:32

Warto zauważyć, że IDE Auto generation może wziął pod uwagę super class, tylko pod warunkiem, że equals() i hashCode() super class jeszcze istnieją. Oznacza to, że należy automatycznie wygenerować te dwie funkcje super najpierw, a następnie automatyczne generowanie dziecka. Mam poniżej prawy przykład pod Intellj Idea:

@Override
public boolean equals(Object o) {
    if (this == o) return true;
    if (o == null || getClass() != o.getClass()) return false;
    if (!super.equals(o)) return false;

    TActivityWrapper that = (TActivityWrapper) o;

    return data != null ? data.equals(that.data) : that.data == null;
}

@Override
public int hashCode() {
    int result = super.hashCode();
    result = 31 * result + (data != null ? data.hashCode() : 0);
    return result;
}

Problem pojawia się tylko wtedy, gdy nie generujesz automatycznie super ' s W pierwszej kolejności. Sprawdź powyżej pod Netbeans.

 0
Author: Ming,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-09-08 06:32:23

Wierzę, że teraz mają metodę, która robi to za Ciebie:

EqualsBuilder.reflectionEquals (this, o);

HashCodeBuilder.reflectionHashCode (this);

 0
Author: Terry H,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-06-06 12:34:11