Java Verständnisfrage zu .equals bei oop - Objekte konvertieren (MOOC.FI)

Thinkpadder

Cadet 1st Year
Registriert
Jan. 2024
Beiträge
11
Hey, mal eine kleine Verständnisfrage zu einer Funktion bei oop, wenn es darum geht, die .equals Methode in einer Klasse neu zu belegen, um damit anstelle einzelner Werte Objekte mit mehreren Variablen miteinander zu vergleichen. An einer Stelle wird dafür der Typ des zu vergleichenden Objekts zu dem des vorliegenden Objekts konvertiert. Dazu hat sich bei mir eine Frage aufgetan, die ich unter dem folgenden Code platziert habe.

Der Code und die Erläuterung stammen aus dem Programmierkurs von der Uni Helsinki (MOOC.FI) - Absatz Comparing the equality of objects (equals)

"The equals method is implemented in such a way that it can be used to compare the current object with any other object. The method receives an Object-type object as its single parameter — all objects are Object-type, in addition to their own type. The equals method first compares if the addresses are equal: if so, the objects are equal. After this, we examine if the types of the objects are the same: if not, the objects are not equal. Next, the Object-type object passed as the parameter is converted to the type of the object that is being examined by using a type cast, so that the values of the object variables can be compared. Below the equality comparison has been implemented for the SimpleDate class"

Code:
public class SimpleDate {
    private int day;
    private int month;
    private int year;

    public SimpleDate(int day, int month, int year) {
        this.day = day;
        this.month = month;
        this.year = year;
    }

    public int getDay() {
        return this.day;
    }

    public int getMonth() {
        return this.month;
    }

    public int getYear() {
        return this.year;
    }

    public boolean equals(Object compared) {
        // if the variables are located in the same position, they are equal
        if (this == compared) {
            return true;
        }

        // if the type of the compared object is not SimpleDate, the objects are not equal
        if (!(compared instanceof SimpleDate)) {
            return false;
        }

        // convert the Object type compared object
        // into a SimpleDate type object called comparedSimpleDate
        SimpleDate comparedSimpleDate = (SimpleDate) compared;

        // if the values of the object variables are the same, the objects are equal
        if (this.day == comparedSimpleDate.day &&
            this.month == comparedSimpleDate.month &&
            this.year == comparedSimpleDate.year) {
            return true;
        }

        // otherwise the objects are not equal
        return false;
    }

    @Override
    public String toString() {
        return this.day + "." + this.month + "." + this.year;
    }
}

Was ich hier nun leider nicht verstehe, ist folgende Zeile:

Code:
        SimpleDate comparedSimpleDate = (SimpleDate) compared;

Wieso ist es an dieser Stelle nötig, den Typ des Objekts zu konvertieren? Ich verstehe, dass es aus irgendeinem Grund notwendig ist, für einen funktionierenden Vergleich zwei Objekte desselben Typs vorliegen zu haben. Aber in diesem Fall werden ja zwei Objekte derselben Klasse miteinander verglichen und müssten - meinem Verständnis nach - gar nicht erst konvertiert werden? Beide Objekte sind vom selben Typ namens "SimpleDate". Ich hoffe, es wird klar, was ich meine. :D
 
Hi,

nach dem Überfliegen hätte ich gesagt, weil du den Parameter bei "public boolean equals(Object compared)" als "Object" übergibst und du vermutlich keinen impliziten Typecast von Object nach SimpleDate machen kannst. Wenn du das einfach zuweisen willst wird es dir vermutlich um die Ohren fliegen.

Aber ich bin weder in Java zuhause noch habe ich das jetzt ausreichend lange angesehen, ist also nur eine Vermutung :)

VG,
Mad
 
  • Gefällt mir
Reaktionen: Thinkpadder
du gibst in equals Object rein, was ja alles sein kann. wie im text beschriben checkst du erst auf gleiche adressen und anschließend ob das objekt vom typ simpleDate ist. wenn ja castest du das objekt zu simpleDate, da nur wenn du das objekt zu simpleDate umwandelst hast du zugriff auf die methoden .getDay() etc. und diese methoden/felder willst du ja auf gleichheit prüfen. castest du nicht dann kommst du zwar an this.getDate() aber nicht an compared.getDate(), da Object keine methode .getDate hat
 
  • Gefällt mir
Reaktionen: Thinkpadder und Drexel
@Madman1209 hat es richtig erklärt. Es wird ein Object übergeben. Ist es keine SimpleDate wird false zurückgegeben (Zeile 32), da es dann definitiv nicht equal sein kann.

Falls es ein SimpleDate ist, wird darauf gecastet und genauer geprüft (ab Zeile 40).
 
  • Gefällt mir
Reaktionen: Thinkpadder
this ist ja das aktuelle Objekt vom Typ SimpleDate und der equals Methode wird das Objekt compared vom Typ Object übergeben. Die Basisklasse Object hat aber nicht dieselben Member wie SimpleDate und folglich würde der folgende Vergleich ab Zeile 40 fehlschlagen, wenn du direkt this.day == compared.day vergleichen würdest. Deswegen wird compared zunächst in den Typ SimpleDate gecastet und im neuen Objekt comparedSimpleDate zwischengespeichert. Mit eben diesem Objekt kann man nun den Vergleich this.day == comparedSimpleDate.day durchführen, weil beides Objekte vom Typ SimpleDate sind und somit beide über dieselben Membervariablen verfügen.

*edit
Ok, ich hatte den Editor zu lange offen und @Madman1209 hat schon längst geantwortet. Hat sich also erledigt ;)
 
  • Gefällt mir
Reaktionen: Thinkpadder
Java:
public boolean equals(Object compared) {
    //...
    SimpleDate comparedSimpleDate = (SimpleDate) compared;
    //...
}
Ich hab zur Veranschaulichung jetzt einfach mal die nicht relevanten Dinge entfernt. Hier siehst du sofort, dass compared vom allgemeinen Typ Object ist und nicht zwangsläufig auch vom Typ SimpleDate. Aus diesem Grund ist der Cast zu SimpleDate nötig - aber eben auch nur dann zulässig, wenn dieses Objekt tatsächlich auch von diesem Typ ist. Mit
if (!(compared instanceof SimpleDate)) {
return false;
}
stellst du genau das sicher: Die Methode endet, falls compared nicht vom Typ SimpleDate ist, sodass der anschließende Cast dann nicht erreicht wird. Andernfalls könnten auch Objekte, die nicht vom Typ SimpleDate sind, den Cast erreichen, was nicht zulässig wäre und wohl zu einem Crash führen würde.

Wurde alles schon gesagt, ich wollte es hiermit aber nochmal etwas veranschaulichen.
 
  • Gefällt mir
Reaktionen: Thinkpadder und Raijin
Vielen Dank für die zahlreichen Antworten! Ich will mich hier mal nicht speziell auf eine Antwort beziehen, da mehrfach belegt wurde, wo mein Denkfehler lag. Meine Frage sehe ich damit als gelöst an. :)
 
  • Gefällt mir
Reaktionen: Raijin
Der Vollständigkeit halber sei noch erwähnt, dass es seit Java 16 (bzw. seit Java 14 als Preview) eine komfortablere Syntax für instanceof mit anschließendem Cast gibt:

Java:
 public boolean equals(Object compared) {
  // if the variables are located in the same position, they are equal
  if (this==compared) {
   return true;
  }

  // if the type of the compared object is not SimpleDate, the objects are not equal
  if (!(compared instanceof SimpleDate comparedSimpleDate)) {
   return false;
  }

  // if the values of the object variables are the same, the objects are equal
  if (this.day==comparedSimpleDate.day &&
    this.month==comparedSimpleDate.month &&
    this.year==comparedSimpleDate.year) {
   return true;
  }

  // otherwise the objects are not equal
  return false;
 }

Oder noch prägnanter:
Java:
 public boolean equals(Object other) {
  return this==other || other instanceof SimpleDate d && day==d.day && month==d.month && year==d.year;
 }

Siehe:
https://blogs.oracle.com/javamagazine/post/pattern-matching-for-instanceof-in-java-14
 
  • Gefällt mir
Reaktionen: SaschaHa
@H4110
Zunächst war ich etwas irritiert, inwiefern
Java:
if (!(compared instanceof SimpleDate comparedSimpleDate)) {
    return false;
}
komfortabler ist als
Java:
if (!(compared instanceof SimpleDate)) {
    return false;
}
, aber worauf du hinaus willst, ist, dass comparedSimpleDate hier als Variable eingeführt wird, richtig? Das ist tatsächlich ganz praktisch, da man sich dann die Zeile SimpleDate comparedSimpleDate = (SimpleDate) compared; spart. Danke für den Hinweis, war mir tatsächlich noch gar nicht bekannt!
 
Zurück
Oben