equals()-Methode Verständnis Frage anhand Code Beispiel

NeoLexx

Mitglied
Hallo Leute, wer kann mir kurz folgendes erklären?

Java:
public class Kunde {
    private int kundennummer;
    private String name;
    private String vorname;
    private String geschlecht;

    public boolean equals(Object obj) {
        if (this == obj) return true;
        if (obj instanceof Kunde) {
            Kunde k = (Kunde) obj;
            return kundennummer == k.kundennummer;
        }
        else 
            return super.equals(obj);
        
        }
    }

Ich habe mehrere Fragen, aber vielleicht erkläre ich erstmal was ich verstehe:

equals()-Methode ist eine Methode der Oberklasse aller Klassen mit dem Namen Object.

Sie wird hier ab Zeile 7 überschrieben.

In Zeile 8 wird geprüft ob das übergeben Objekt obj, das gleiche ist wie die Instanz von Kunde, die diese Methode aufruft. Falls ja, dann wird true ausgegeben.

In Zeile 9 wird eine bedingte Anweisung erzeugt, falls obj und Kunde von gleichen Typ sind, dann wird in Zeile 10 ein Variable k von Typ Kunde erzeugt, auf die dann obj gecastet wird.
An dieser Stelle verstehe ich nicht genau was damit bewirkt wird, denn man hat doch vorher schon geprüft ob obj vom Typ Kunde ist, warum muss es jetzt nochmals auf Kunde gecastet werden. Bei der Gelegenheit, vielleicht kann mir einer genau erklären was bedeutet es, wenn ich auf ein bestimmten Typ caste?

In Zeile 11 werden nun, mittels Gleichheitsoperator, die beiden Werte, der Variablen von der aufrufenden Instanz und der vergleichenden Instanz, verglichen. Sind sie identisch so wird ture ausgegeben, andernfalls false.

Dann, in Zeile 14, wird, sofern die Bedingung aus der bedingten Anweisung aus Zeile 9 false ist, das ganze an die equals()-Methode der Klasse Object übergeben. Und an dieser Stelle verstehe ich nicht wieso das noch getan werden muss. Denn immerhin haben wir nun als Parameter, ein nicht Kompatiblies Objekt zur Instanz der Klasse Kunde angegeben, wieso geben wir nach else nicht einfach ein return false aus?
 
Hier ist das etwas verkürzt
Java:
public class Kunde {
    private int kundennummer;
    private String unwichtig;
    public Kunde(int kundennummer, String unwichtig) {
        Objects.requireNonNull(unwichtig);
        this.kundennummer = kundennummer;
        this.unwichtig = unwichtig;
    }
    @Override
    public int hashCode() {
        return Objects.hash(kundennummer);
    }
    @Override
    public boolean equals(Object obj) {
        if (obj != null && obj instanceof Kunde)
            return kundennummer == ((Kunde) obj).kundennummer;
        return super.equals(obj);
    }
}
 
Wo ist der null check? 😉
Braucht man nicht, da instanceof nur bei non null Objekten true liefert.

Hallo Leute, wer kann mir kurz folgendes erklären?


In Zeile 9 wird eine bedingte Anweisung erzeugt, falls obj und Kunde von gleichen Typ sind, dann wird in Zeile 10 ein Variable k von Typ Kunde erzeugt, auf die dann obj gecastet wird.
An dieser Stelle verstehe ich nicht genau was damit bewirkt wird, denn man hat doch vorher schon geprüft ob obj vom Typ Kunde ist, warum muss es jetzt nochmals auf Kunde gecastet werden. Bei der Gelegenheit, vielleicht kann mir einer genau erklären was bedeutet es, wenn ich auf ein bestimmten Typ caste?
Nun, du möchtest auf das Attribut kundennummer zugreifen. Das gibt es aber nicht in der Klasse Object - sondern nur in der Klasse Kunde. Also musstest du das Objekt auf den Typ casten um auf das Attribut zugreifen zu können.
In Zeile 11 werden nun, mittels Gleichheitsoperator, die beiden Werte, der Variablen von der aufrufenden Instanz und der vergleichenden Instanz, verglichen. Sind sie identisch so wird ture ausgegeben, andernfalls false.
Korrekt.

Dann, in Zeile 14, wird, sofern die Bedingung aus der bedingten Anweisung aus Zeile 9 false ist, das ganze an die equals()-Methode der Klasse Object übergeben. Und an dieser Stelle verstehe ich nicht wieso das noch getan werden muss. Denn immerhin haben wir nun als Parameter, ein nicht Kompatiblies Objekt zur Instanz der Klasse Kunde angegeben, wieso geben wir nach else nicht einfach ein return false aus?
Weil der Programmierer es unnötig komplex machen wollte. Wenn du dir den Code von Object.equals ansiehst:
Java:
    public boolean equals(Object obj) {
        return (this == obj);
    }
Dann wäre ein return false; an der Stelle auch vollkommen ausreichend.
 
Super danke für die schnellen Antworten. 👍

Hier ist das etwas verkürzt
Java:
public class Kunde {
    private int kundennummer;
    private String unwichtig;
    public Kunde(int kundennummer, String unwichtig) {
        Objects.requireNonNull(unwichtig);
        this.kundennummer = kundennummer;
        this.unwichtig = unwichtig;
    }
    @Override
    public int hashCode() {
        return Objects.hash(kundennummer);
    }
    @Override
    public boolean equals(Object obj) {
        if (obj != null && obj instanceof Kunde)
            return kundennummer == ((Kunde) obj).kundennummer;
        return super.equals(obj);
    }
}
Danke für deine verkürzte Variante, aber mir ging es darum den Code den ich im Script so übergeben bekommen hatte zu verstehen.
Dann wäre ein return false; an der Stelle auch vollkommen ausreichend.
Echt jetzt? Das machen die doch mit Absicht! Aber vielleicht besser so, so wird man dann doch gezwungen etwas genauer nachzudenken 🙄.
Nun, du möchtest auf das Attribut kundennummer zugreifen. Das gibt es aber nicht in der Klasse Object - sondern nur in der Klasse Kunde. Also musstest du das Objekt auf den Typ casten um auf das Attribut zugreifen zu können.
Wir das Casten noch in anderen Situationen benötigt, außer in der wo man eine Methode einer Unterklasse aufrufen möchte die nicht in der Oberklasse vorkommt?
 
Wir das Casten noch in anderen Situationen benötigt, außer in der wo man eine Methode einer Unterklasse aufrufen möchte die nicht in der Oberklasse vorkommt?
Immer dann, wenn du auf Eigenschaften (Felder, Methoden) einer Unterklasse zugreifen willst. Im Idealfall vermeidet man Casten soweit es geht, weil das ein Hinweis auf eine schlechte Architektur sein kann. Denn wozu nutze ich Objekte einer Oberklasse, wenn ich dann doch wieder auf die Unterklasse casten muss.

Vermeiden kann man es aber nie, insbesondere bei so Dingen wie equals, die zum einen aus einer Zeit vor den Generics stammen und die zum anderen auch sehr flexibel sein sollen.
 
Weil der Programmierer es unnötig komplex machen wollte. Wenn du dir den Code von Object.equals ansiehst:
Java:
    public boolean equals(Object obj) {
        return (this == obj);
    }
Dann wäre ein return false; an der Stelle auch vollkommen ausreichend.
Wobei ein Frage hätte ich dann doch noch dazu. Wenn ich also zum Schluss auf die Methode der Oberklasse zugreife und dort per == prüfen lasse ob this == obj ist, bezieht sich das "this" doch dann auf die Instanz von Object oder nicht (wobei welche Instanz, habe doch keine erzeugt)? Und sagen wir mal ich würde nun als Object obj tatsächlich eine Instanz von Object übergeben, dann würde doch letztendlich ein true bekommen, oder etwa nicht?
 
der statische Typ von this ist Object, der dynamische Typ (der ja letztendlich zählt) ist Kunde
 
der statische Typ von this ist Object, der dynamische Typ (der ja letztendlich zählt) ist Kunde
Dir ist aber schon klar, dass ich mich bei meiner Frage auf Zeile 14 des Codes beziehe?

Java:
public class Kunde {
    private int kundennummer;
    private String name;
    private String vorname;
    private String geschlecht;

    public boolean equals(Object obj) {
        if (this == obj) return true;
        if (obj instanceof Kunde) {
            Kunde k = (Kunde) obj;
            return kundennummer == k.kundennummer;
        }
        else
            return super.equals(obj);
       
        }
    }
Und dass ich bei der Fragestellung von dem Szenario ausgehe, dass ich der Methode eine Objekt des Typs Object zur Prüfung übergebe?
 
Zuletzt bearbeitet:
ja stimmt 😉
Man könnte noch etwas genauer sein... der statische Typ von this ist zum Übersetzungszeitpunkt Object und der dynamische Typ von this ist in diesem konkreten Fall zur Laufzeit Kunde.
Also trifft auf das Kunde-Objekt folgendes zu, wenn super.equals darauf aufgerufen wird: a widening (assignment) reference conversion
 
dass ich der Methode eine Objekt des Typs Object zur Prüfung übergebe?
Der Typ von obj bleibt immer gleich, diesen castest du ja nicht...

Das problematische an meiner equals Methode ist nur, dass eine Kunde-erweiternde Klasse ebenfalls gleich sein kann zur Klasse Kunde und das manchmal nicht gewollt ist:
Java:
    public static void main(String[] args) {
        CD cd1 = new CD(2);
        AB cd2 = new AB(2);
        CD cd3 = new CD(3);
        System.out.println(cd1.equals(cd2));
        System.out.println(cd2.equals(cd1));
        System.out.println(cd3.equals(cd1));
        System.out.println(cd1.equals(cd3));
    }
}

class AB {
    private int id;

    public AB(int id) {
        this.id = id;
    }

    @Override
    public int hashCode() {
        return id;
    }

    @Override
    public boolean equals(Object obj) {
        if (obj instanceof AB)
            return id == ((AB) obj).id;
        return this == obj;
    }
}

class CD extends AB {
    public CD(int id) {
        super(id);
    }
}
 
Ich muss nochmal Fragen, da ich glaube dass meine Frage nicht ganz angekommen ist. Folgender Code ist gegeben:
Java:
public class Kunde {
    private int kundennummer;

public Kunde (int kundennnummer) {
this.kundennummer=kundennumer;
}
    public boolean equals(Object obj) {
        if (this == obj) return true;
        if (obj instanceof Kunde) {
            Kunde k = (Kunde) obj;
            return kundennummer == k.kundennummer;
        }
        else
            return super.equals(obj);
     
        }
    }

In einer anderen Klasse mach ich nun folgendes:

Java:
public class irgendEineKlasse{
    Kunde kd1 = new Kunde(1);
    Object ob1 = new Object();
    boolean wert = kd1.equals(ob1);
 
}

Dann bekomme ich doch ein true raus, oder nicht?

Edit:
Und zwar erst wenn das Ganze in Zeile 14 an die Oberklasse Object weiter gereicht wurde.
 
Zuletzt bearbeitet:
Nein, da kommt false raus. Was durch den Super-Aufruf am Ende zurückgegeben wird ist im Endeffekt

kd1 == obj - was wiederrum false ist.
Okay, danke für die konstruktive Antwort.

Das Scirpt anhand dessen ich mich gerade durcharbeite, gibt das nicht her, worauf sich das "this" aus der equals()-Methode der Oberklasse Object bezieht. Intuitiv wäre meine Vermutung, dass es sich auf die Oberklasse selbst bezieht und ich damit ein Object == obj prüfe und damit auch ein true erhalte. Immerhin, falls man in einer Mehtode this.variable benutzt, bezieht es sich auf eine Instanzvariable, daher auch erstmal meine Annahme, dass es sich auf die Klasse bezieht, die diese Methode implementiert hat.

Aber das Kapitel werde ich erstmal ruhen lassen und mich weiter an die Vorgehensweise des Skriptes halten. Eventuell wird darauf in kürze ja eingegangen.



Danke nochmal

Edit: Wobei ich gerade sehe, dass der Vergleichsoperator im Bezug auf Komplexe Datentypen immer zwei Objekte vergleicht und in dem Fall existieren ja nur diese zwei Objekte.
 
Zuletzt bearbeitet:
this heißt immer dieses Objekt.

Das heißt es bezieht sich nicht auf die Klasse, sondern die konkrete Instanz. Du kannst this (im Prinzip) auch nur innerhalb einer Instanz-Methode nutzen.

Beispiel
Java:
public class MyClass {
   public int variable;

  public void macheWas() {
    System.out.println(this.variable);
  }
}

// ...
Irgendwo anders
MyClass myObject = new MyClass();
myObject.variable = 2;
myObjkect.macheWas();
Das gibt den Wert 2 aus.

Wie du siehst, kann man auf Member Variablen eines Objektes mittels name.variable zugreifen.
Wenn du aber innerhalb eines Objektes bist (in der Methode macheWas), kannst du entweder den Namen vor dem Punkt weglassen. Oder - um dich auf das konkrete Objekt zu beziehen, in desen Methode du gerade bist - this davorstellen.
 
Java:
public class Kunde {
  private int kundennummer;

public Kunde (int kundennnummer) {
  this.kundennummer=kundennumer;
}
public boolean equals(Object obj) {
  if (this == obj) return true;
  if (obj instanceof Kunde) {
   Kunde k = (Kunde) obj;
   return kundennummer == k.kundennummer;
  }
  else
  return super.equals(obj);

  }
}

//andere Klasse
public class irgendEineKlasse{
  Kunde kd1 = new Kunde(1);
  Object ob1 = new Object();
  boolean wert = kd1.equals(ob1);
}
//die Klasse Object, die Große, Vater und Mutter aller anderen Klassen XD

public class object{
....

public equals(Object obj) {
  return (this == obj);
Wie du siehst, kann man auf Member Variablen eines Objektes mittels name.variable zugreifen.
Wenn du aber innerhalb eines Objektes bist (in der Methode macheWas), kannst du entweder den Namen vor dem Punkt weglassen. Oder - um dich auf das konkrete Objekt zu beziehen, in desen Methode du gerade bist - this davorstellen.
Ja, das habe ich auch geblickt, denn das "this" kommt ja in der von dir beschriebenen Funktion, in der überschriebenen equals()-Methode von Kunde vor.
Wenn ich später in der Klasse "irgendEineKlasse" diese equals()-Methode aufrufe (Zeile 23) dann wird kd1 und obj mittels == verglichen, da "this" hier auf die Klasse Kunde verweist. Hab ich auch verstanden.

Wenn ich dann aber das Ganze in Zeile 14 an die equals()-Methode der Oberklasse schicke, dann bezieht sich das "this" (Zeile 31) der equals()-Methode der Oberklasse (hier Object), immer noch auf kd1. Verweist also nicht auf die Klasse Obejct, ähnlich wie es das in der Klasse Kunde tut.

Aber das wird was mit den statischen und dynamischen Typen zu tun haben, die BestGoakkeeper bereits ansprach, ein Thema mit dem ich mich zu gegebenen Zeitpunkt auseinander setzen werde, zumal es, wie bereits gesagt, bisher im Skript nicht Thematisiert worden ist.
 
Ja, sorry. Sich über Programmiersprachen verbal auszutauschen, Bedarf höchster sprachlicher Präzision, daran muss man sich erstmals gewöhnen. Bis dahin werde ich wohl, sofern andere Fragen aufkommen werden, mehrmals Formulieren müssen. Aber ich gebe mein bestes mich verständlicher auszudrücken.
 

Zurück
Oben