Interface für Verhalten von "=="?

Djinndrache

Bekanntes Mitglied
Angenommen wir haben eine simple User Class.
Java:
class User {
private int userID;
//konstruktor setzt userID
}


Man kann prüfen ob zwei Userobjekte identisch sind indem man einfach prüft:
Java:
User user = new User(1);
User user2 = user;
if(user == user2){
// true
}
User user3 = new User(1);
if(user == user3){
// false :(
}

Aber sagen wir mal ich habe jetzt nicht unbedingt immer identische Userobjekte, und dennoch sind sie inhaltlich gleich. Ich möchte die Klasse User jetzt irgendwie so erweitern (gibts dafür bestimmte Interfaces?), dass der "=="-Operator anders behandelt wird. Zum Beispiel soll "user == user3" true sein, wenn die userID der objekte identisch sind.

Gibt es irgendein Interface das man implementieren kann, mit dem man user dann erweitert durch eine Methode à la:
Java:
@Override
public boolean equals(User other){
return this.getID() == other.getID();
}



Mir ist natürlich klar, dass ich die Methode equals() einfach so nehmen könnte zum vergleichen von Usern. Mich interessiert nur ob man quasi das Verhalten von "==" durch irgend ein Interface oder so verändern kann.
 
Zuletzt bearbeitet:
Wenn du jede Operation nach belieben ändern würdest, wie soll jemand anders (oder auch du in einen Monat oder vielleicht sogar Jahr) das nachvollziehen können?
In Java weiß man immer genau was die Operationen tun. Es haben zwar ein paar Operationen mit mehreren Funktionen (+ addieren von Zahlen und zusammenfügen von Strings, & booleanische and ohne Kurzschluss und bitweises and, | booleanisches or ohne Kurzschluss und bitweises or), aber man weiß genau, was Object o = (new Object() + new Object()) machen würde. (Nen Fehler auspucken)

Interface haben auch nichts mit den Operatoren zu tun, die stellen nur sicher, dass eine bestimmte Methode implementiert ist oder markieren eine Klasse für einen bestimmten Zweck (z. B. Clonable oder Serializable). Beispielsweiße kann man eine ArrayList oder eine LinkedList in eine List-Variable (List ist das Interface) schreiben, solange man nur Methoden braucht die in List definiert sind. So kann man bequem list = new ArrayList<?>(); in list = new LinkedList<?>(); ändern, ohne einen anderen Teil des Codes auszutauschen. Geht natürlich auch zur Laufzeit.
Nennt sich wenn ich mich nicht täusche "Gegen das Interface programmieren".
 
Zudem nutzen die meisten libs von java selbst die equels-methode.
wenn du versuchst == zu überschreiben, verbaust du dir viele funktionen der java-libs.

ACHTUNG Überschreib immer die Equels-Methode von Object (in deinem beispiel schreibst du eine neue und überschreibst nicht), denn nur diese wird letztlich genutzt.
(equals(Objekt o))

könnte bei dir dann so aussehen:
Java:
public boolean equals(Object o)
{
     if(!o instanceof User) return false;
     return this.getID() == other.getID();
}
 
In C# funktioniert das Operator überladen. Es ist auch oft hilfreich, kann natürlich auch zu den erwähnten Problemen führen (was passiert bei diesem oder jenem '==').

Ist ein Framework entsprechend dokumentiert, sind die Überladungen kein Problem. Zuviele Überladungen (bzw. schlecht dokumentierte) sind natürlich sinnfrei.
 
Zudem nutzen die meisten libs von java selbst die equels-methode.
wenn du versuchst == zu überschreiben, verbaust du dir viele funktionen der java-libs.

ACHTUNG Überschreib immer die Equels-Methode von Object (in deinem beispiel schreibst du eine neue und überschreibst nicht), denn nur diese wird letztlich genutzt.
(equals(Objekt o))

könnte bei dir dann so aussehen:
Java:
public boolean equals(Object o)
{
     if(!o instanceof User) return false;
     return this.getID() == other.getID();
}

Dazwischen fehlt noch
Java:
User other = (User) o;
aber ich weiß was du meinst. Danke 🙂
 
Normalerweise generiert dir die IDE deiner Wahl auf Wunsch die Methoden [c]equals()[/c] und [c]hashCode()[/c]. Kann so aussehen:
Java:
public class Foo {

    private String foo;
    private Object bar;
    private int number;

    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + (bar == null ? 0 : bar.hashCode());
        result = prime * result + (foo == null ? 0 : foo.hashCode());
        result = prime * result + number;
        return result;
    }

    @Override
    public boolean equals(final Object obj) {
        if (this == obj) {
            return true;
        }
        if (obj == null) {
            return false;
        }
        if (getClass() != obj.getClass()) {
            return false;
        }
        Foo other = (Foo) obj;
        if (bar == null) {
            if (other.bar != null) {
                return false;
            }
        }
        else if (!bar.equals(other.bar)) {
            return false;
        }
        if (foo == null) {
            if (other.foo != null) {
                return false;
            }
        }
        else if (!foo.equals(other.foo)) {
            return false;
        }
        if (number != other.number) {
            return false;
        }
        return true;
    }
}
 
na sowas würde ich mir nicht generieren lassen 😉 ,
lieber doch erst die ints vergleichen, das geht schnell,

für jedes Objekt-Attribut 8 Zeilen ist auch traurig, da reicht eine:
Java:
if (!Helper.equal(foo,other.foo)) return false;
in der Hilfsmethode dann auf null prüfen und gegebenenfalls equals aufrufen

hashCode() immerhin einzeilig
 
Möglicherweiße gibts da irgendwelche Kniffe die ich noch nicht kenne, aber das Meiste ist wohl nicht notwendig. ^^
Man braucht this nicht auf null zu testen, da man auf null keine Instanzmethode aufrufen kann. instanceof prüft ob es die selbe Klasse ist (oder eine Subklasse) und enthält einen null-Check für den Parameter. Das einzige was man Daassan Methode noch hinzufügen könnte wäre ein Vergleich mit super.equals (wenn von Object erbt ist dass das Selbe wie ==, wenn nicht werden auch die Variablen der Superklasse geprüft, sofern diese richtig überschreibt), aber das war dann glaub ich alles.
 
Falsch, denn [c]instanceof[/c] kann zu Verletzungen der wie heissen diese Regeln schon wieder? führen. Wenn eine Klasse [c]Bar[/c] von [c]Foo[/c] abgeleitet ist, könnte mit [c]instanceof[/c] theoretisch [c]Foo#equals(Bar) == true[/c] und [c]Bar#equals(Foo) == false[/c] passieren!
 
Das stimmt natürlich. War der selbe Fehler wie der verallgemeinerlichte super-Aufruf. Ich hab die Klasse als geschlossenes (Object -|> Klasse) System und nicht in einer potentiell größeren Vererbungshierarchie betrachtet.

Danke, so genau hab ich mir darüber noch nie Gedanken gemacht.
 
Habs auch lange mit [c]instanceof[/c] gemacht, aber jetzt weiss ich es besser und machs erst noch nicht mehr von Hand 😉 Kommt auf diese paar Zeilen mehr oder weniger (am Ende der Klasse) auch nicht drauf an...
 
speziell equals sollte man auch nicht gerade so unerwartet implementieren, dass etwa der allgemeine Contract verletzt wird,
dann schon eine eigene Methode, gleich mit passenden Namen, so wird der Leser bestens vorbereitet

ob ein Objekt ein und dasselbe wie ein anderes ist, ist eine immer benötigte Standardfunktion,
egal welche Klasse, allein schon bei allgemeiner Verarbeitung wie Einfügen in eine Liste,
die ist in der Sprache unter == untergebracht und sollte nicht zerstört werden
 
In C++ kann man sogar den Zuweisungsoperator überladen (und eigentlich alle, bis auf ganz wenige Ausnahmen), und ersteres muss oder sollte man sogar meistens. Die Komplexität, die der Sprache mit Operatorüberladung aufgebürdet wird, ist dabei ein mögliches Gegenargument. Viel wichtiger finde ich persönlich, dass so ein Feature tendenziell auch von Leuten genutzt wird, die meinen, ein Ring wäre so ein rundes Ding, eine Gruppe mehrere Leute, und ein Körper das, was sie unter dem Hals haben.
 
Das ist doch Quark. Höre diese "Begründung" so oft, nur wahrer wird sie dadurch nicht. Ich mein, ein equals() kann ich auch falsch oder unerwartet implementieren, da stört das aber niemanden?
ich rede hier nicht von falscher oder richtiger Implementierung. Wenn du equals siehst gehst du von der Gleichheitspruefung aus, unabhaengig davon ob sie richtig implementiert ist.

Bei == geht man von der identiaetspruefung aus, bei + von addition etc.

Principle of least astonishment ist hier meine Begruendung, man hat bei manchen Sprachkonstruktoren Erwartungen, die man als Entwickler nicht brechen sollte
 

Zurück
Oben