Objektgleichheit im Vererbungsfall

Warum muss in der .equals() Methode einer abgeleiteten Klasse immer auch super.equals() aufgerufen werden?

Zunächst: Es muss nicht, aber in fast allen Fällen stimmt sonst die Semantik von .equals() nicht.

Dazu folgendes kleine Beispiel:

class Basis {
   int a;
   Basis(int a) { this.a = a; }
   @Override
   public boolean equals(Object other) {
      if (other == null) return false;  // von nichts kommt nichts.
      if (this == other) return true;   // Identität
      if (getClass() != other.getClass()) return false;  // Klassen passen nicht
      // nur eigene Elemente vergleichen
      // hier technisch gesehen ok, da Basisklasse Object!
      return this.a == ((Basis) other).a;
   }
}
class Abgeleitet {
   char b;
   Abgeleitet(int a, char b) { 
      super(a);
      this.b = b;
   }
   @Override
   public boolean equals(Object other) {
      if (other == null) return false;  // von nichts kommt nichts.
      if (this == other) return true;   // Identität
      if (getClass() != other.getClass()) return false;  // Klassen passen nicht
      return b == ((Abgeleitet) other).b;  // nur eigene Elemente vergleichen
   }
}

Wenn man nun .equals() verwendet, hat das den eigenartigen (i.d.R. ungewünschten) Effekt:

Abgeleitet a1 = new Abgeleitet(3, 'a');
Abgeleitet a2 = new Abgeleitet(4, 'a');
System.out.println(a1.a == a2.a);   // false
System.out.println(a1.equals(a2));  // true?!

Das ist unerwartet, da ja beide Instanzen ein unterschiedliches a haben (3 und 4). Also muss man super.equals(other) aufrufen, um beim Vergleich auch die Daten in der Superklasse mit in Betracht zu ziehen.

Die korrekte Implementierung von .equals in der abgeleiteten Klasse ist also

@Override
public boolean equals(Object other) {
   if (other == null) return false;  // von nichts kommt nichts.
   if (this == other) return true;   // Identität
   if (getClass() != other.getClass()) return false;  // Klassen passen nicht

   // Inhalte der Basisklasse müssen übereinstimmen
   if (!super.equals(other)) return false;

   return b == ((Abgeleitet) other).b;  // nur eigene Elemente vergleichen
}
Written on March 30, 2017