Sie kennen bereits gewöhnliche Methoden von Klassen. Es gibt aber auch speziellere Methoden, die Sie in Ihre Klassen aufnehmen können. Dazu gehören equals, hashCode und toString. Doch was macht diese Methoden so speziell, dass es uns ein komplettes Kapitel wert ist? Dies werden Sie schon bald herausfinden!
Ursprung
Zuerst einmal gilt zu klären, woher diese Methoden überhaupt kommen, wo liegt ihr Ursprung und warum heißen sie genau so und nicht anders? Nun, jede Klasse, die nicht explizit von einer Klasse erbt (Hierzu lernen Sie im Kapitel über Vererbung mehr), stammt automatisch von der Klasse Object ab. Folglich erbt jede Klasse von Object oder einer Subklasse von Object und erhält deshalb auch alle Methoden von Object – u. a. equals, hashCode und toString. Sie können also davon ausgehen, dass jedem Objekt irgendeiner Klasse diese drei Methoden zur Verfügung stehen. Oftmals reicht es aber nicht aus, die Implementierung der Object-Klasse beizubehalten. Dann müssen diese Methoden auf Ihre eigene Art und Weise umgesetzt werden. Siehe hierzu auch das Kapitel über Polymorphie.
equals
Die equals-Methode kennen Sie bereits vom Vergleichen zweier Strings. In diesem Kapitel wurde Ihnen erklärt, warum Strings bzw. allgemein Objekte nicht über den == Operator auf inhaltliche Gleichheit überprüft werden können (es werden mit == lediglich die Referenzen geprüft), sondern warum dafür die equals-Methode verwendet werden muss. Wenn Sie also zwei Objekte vergleichen möchten, müssen Sie immer equals verwenden. Dies funktioniert tadellos für Objekte von Klassen, die diese Methode überschrieben haben – was Ihre eigene Klasse von Natur aus nicht tut. Wenn Ihre eigene Klasse auf inhaltliche Gleichheit überprüft werden soll, dann müssen Sie die equals-Methode in Ihre Klasse aufnehmen und ausprogrammieren. Hierdurch teilen Sie Java mit, welche Attribute miteinander verglichen werden müssen, um eine inhaltliche Gleichheit zu bestätigen.
Sehen wir uns zunächst einmal den Kopf dieser Methode an:
public boolean equals(Object obj)
Es handelt sich also um eine öffentliche Methode mit einem boolean als Rückgabetyp (stimmen die beiden Objekte überein? => true <= stimmen die Objekte nicht überein? => false <=) und einem Objekt als Parameter. Dieses Objekt der Klasse Object ist immer das Objekt, mit dem das Objekt, von welchem die equals-Methode aus aufgerufen wurde, verglichen werden soll.
Wird diese Methode nicht überschrieben, so wird der Standard-Code aus der Object-Klasse verwendet. Dieser sieht so aus:
public boolean equals(Object obj) {
return (this == obj);
}
Sie sehen, dass der Standard-Code nicht gerade effektiv ist und nur true für Objekte mit der selben Referenz zurückliefert. Dieses Verhalten wollen wir nun ändern. Dazu erstellen wir eine neue Klasse Bottle, welche eine Flasche repräsentiert sowie einen Inhalt (Wasser, Cola, Bier, …) und eine Kapazität (0,33 Liter, 0,5 Liter, 1,0 Liter, …) besitzt:
public class Bottle {
private float capacity = 0;
private String content = null;
o
public float getCapacity() {
return this.capacity;
}
o
public void setCapacity(float capacity) {
this.capacity = capacity;
}
o
public String getContent() {
return this.content;
}
o
public void setContent(String content) {
this.content = content;
}
}
Wenn Sie nun neue Objekte dieser Klasse anlegen und selbige mit identischen Daten füttern, sollten die Objekte den selben Inhalt besitzen. Dies überprüfen Sie mit equals.
public static void main(String[] args) {
o
Bottle b1 = new Bottle();
Bottle b2 = new Bottle();
Bottle b3 = new Bottle();
b1.setContent("Wasser");
b1.setCapacity(1.0F);
b2.setContent("Wasser");
b2.setCapacity(1.0F);
b3.setContent("Cola");
b3.setCapacity(0.5F);
o
if (b1.equals(b2)) {
System.out.println("Flasche1 hat den selben Inhalt wie Flasche2");
}
else {
System.out.println("Flasche1 hat NICHT den selben Inhalt wie Flasche2");
}
if (b2.equals(b3)) {
System.out.println("Flasche2 hat den selben Inhalt wie Flasche3");
}
else {
System.out.println("Flasche2 hat NICHT den selben Inhalt wie Flasche3");
}
}
An der Konsolenausgabe sehen Sie aber, dass die beiden Flaschen (b1 und b2) mit identischem Inhalt von Java nicht als identisch angesehen werden.
Flasche1 hat NICHT den selben Inhalt wie Flasche2
Flasche2 hat NICHT den selben Inhalt wie Flasche3
Wie Sie sich sicher denken können, muss an dieser Stelle die equals-Methode überschrieben werden. Hierzu müssen Sie sich zuerst überlegen, was eine Flasche im Vergleich zu einer anderen Flasche inhaltlich identisch macht. Diese Punkte sind schnell gefunden:
- Es muss sich die gleiche Flüssigkeit (
content) in beiden Flaschen befinden - Es muss sich gleich viel Inhalt (
capacity) in beiden Flaschen befinden
Mit diesem Wissen, können wir die equals-Methode in unserer Bottle-Klasse implementieren:
public boolean equals(Object obj) {
o
// Vergleich von capacity
// Vergleich von content
return false;
}
Damit die Werte verglichen werden können, müssen wir das übergebene Objekt in unsere Bottle-Klasse casten.
Casting wurde das erste Mal in Kapitel 02.03. Primitive Datentypen angesprochen und wird Ihnen detailliert in Kapitel 04.05 Vererbung erklärt.
Anschließend sollte es für Sie mit Ihren jetzigen Kenntnissen eine Kleinigkeit sein, den Rest der Methode auszuprogrammieren:
public boolean equals(Object obj) {
o
Bottle b = (Bottle)obj;
if (this.capacity == b.getCapacity() && this.content.equals(b.getContent())) {
return true;
}
return false;
}
Ihre Klasse sieht nun so aus:
public class Bottle {
private float capacity = 0;
private String content = null;
public float getCapacity() {
return this.capacity;
}
public void setCapacity(float capacity) {
this.capacity = capacity;
}
public String getContent() {
return this.content;
}
public void setContent(String content) {
this.content = content;
}
public boolean equals(Object obj) {
Bottle b = (Bottle)obj;
if (this.capacity == b.getCapacity() && this.content.equals(b.getContent())) {
return true;
}
return false;
}
}
Wenn Sie nun noch mal Ihre Main-Methode ausführen, können Sie die Veränderungen sehen:
Flasche1 hat den selben Inhalt wie Flasche2
Flasche2 hat NICHT den selben Inhalt wie Flasche3
Aber warum ist es jetzt so wichtig diese Methode equals zu nennen und Ihr ein Objekt zu übergeben? Nun, wenn Ihre Klasse von anderen Klassen verwendet wird, werden diese standardmäßig die equals-Methode mit einem Objekt der Klasse Object oder einer Kindklasse als Übergabeparameter verwenden - und nicht Ihre eigene Methode mit einem anderen Methodenkopf!
Da der equals-Methode ein Objekt einer beliebigen Klasse übergeben werden könnte, sollten Sie in der Praxis (bevor Sie casten) überprüfen, ob es sich auch tatsächlich um zwei identische Klassen handelt. Dies können Sie durch einen Vergleich des Rückgabewerts der Methode getClass (welche ebenfalls durch Object implementiert wird) erreichen. Zusätzlich sollten Sie überprüfen, ob nicht null, oder die Referenz auf das selbe Objekt übergeben wurde. Unsere neue equals-Methode würde dann so aussehen:
public boolean equals(Object obj) {
if (obj == null) {
return false;
}
if (obj == this) {
return true;
}
if (obj.getClass() == this.getClass()) {
Bottle b = (Bottle)obj;
if (this.capacity == b.getCapacity() && this.content.equals(b.getContent())) {
return true;
}
}
return false;
}
Auf der nächsten Seite finden Sie Informationen zu hashCode, toString und den Verträgen für hashCode und equals
