// Das hier ist ein Musterobjekt, das mit HashSets, TreeSets, Lists etc... korrekt funktioniert // Cloneable sagt aus, dass wir unser Objekt ueber die Methode clone aus Object clonen koennen. // Cloneable "taggt" dabei nur das Objekt, definiert selbst aber keine Methoden. // Ohne clonable wirft die clone-Methode eine CloneNotSupportedException. public class NumberObject implements Comparable, Cloneable { // D.h. wir koennen unser NumberObject mit anderen NumberObjects vergleichen // Das ist wichtig fuer SortedSets und TreeMap private int value; public NumberObject(int value) { this.value = value; } @Override // die folgende Methode ist aus dem Comparable-Interface und wird ueberlagert [ab Java 6] public int compareTo(NumberObject that) { // siehe auch http://java.sun.com/j2se/1.5.0/docs/api/java/lang/Comparable.html // <0 : this ist kleiner als that // 0 : this ist gleich that [muss mit equals konsistent sein] // >0 : this ist groesser als that // nicht vergleichbar gibts nicht // Mit Zahlen geht das leicht return this.value - that.value; } // Die folgenden Methoden sind aus Object und somit in jedem Objekt enthalten. // Erzeugt ein neues Object mit gleichem Wert @Override // ist nicht noetig, erzeugt aber einen Compilerfehler, wenn man was falsch macht (sozusagen fail-fast) public Object clone () throws CloneNotSupportedException { return super.clone(); // Object.clone kopiert selbststaendig alle Felder und erzeugt ein neues Objekt. // clone ist eigentlich protected, aber wir wollen, dass jeder unser Objekt // clonen kann. // Ist uebrigens eine native-Methode, die keinen Konstruktor aufruft und stellt // so sicher, dass auch Unterklassen richtig zurueckgeliefert werden. // Member werden uebrigens nicht geklont, wenn man das moechte, diese ebenfalls // ueber clone() clonen. } // Wenn man in Collections Objekte suchen moechte sollte man folgende equals-Methode // ueberlagern @Override // Bei Java 5.0 gabs das bei Interfaces noch nicht. public boolean equals(Object o) { // und nicht equals(NumberObject o)! // Die Parameter muessen in Java gleich bleiben, // theoretisch duerften sie Contravariant sein [aber dazu in OOP mehr] // Wenn sie identisch sind, sind sie auch gleich if(this == o) { // ist eigentlich nicht noetig, spart aber Zeit return true; } if(o == null || this.getClass() != o.getClass()) { // Wenn o null ist oder beide einen unterschiedlichen Typ haben return false; } // Ansonsten ist o ein NumberObject NumberObject that = (NumberObject) o; // Vergleiche alle Member in this und that miteinander return this.value == that.value; } // Man sieht auch haeufig folgende Implementierung: /* public boolean equals(Object o) { if(this == o) { return true; } if(o instanceof NumberObject) { NumberObject that = (NumberObject) o; return this.value == that.value; } return false; } */ // Diese kann allerdings die Symmetrie-Eigentschaft verletzen: // http://java.sun.com/javase/6/docs/api/java/lang/Object.html#equals(java.lang.Object) // for any non-null reference values x and y, x.equals(y) should return true if and only if y.equals(x) returns true. @Override // das ist vorallem hier praktisch, da man gern vergisst, ob das c gross oder klein ist public int hashCode() { // Ein Zahlencode. Wenn 2 Objekte gleich sind, muss auch deren hashCode gleich sein. // Ansonsten sollte er sich aus Effizienzgruenden so oft wie moeglich unterscheiden. // Wird in HashMaps und HashSets benutzt. Defacto ist eine HashMap ein Array von Listen, // wobei der Index vom Array der HashCode ist. Sprich, je mehr Objekte mit gleichem // hashCode auftreten, desto mehr Elemente muessen in der Liste verglichen werden. // hier ist das leicht. return value; // Wenn man mehrere Member hat folgende Empfehlung: // Angenommen Member sind die Objekte a, b und c, suche eine Primzahl [String verwendet 17] // und Horner-Schema // return ((a.hashCode() * 17 + b.hashCode()) * 17 + c.hashCode()) * 17; // Wem das ganze egal ist, der kann natuerlich auch // return 0; hinschreiben, erfuellt alle Voraussetzungen, ist aber nicht effizient. } // Und die letzte Methode wird automatisch aufgerufen, wenn // man das Objekt in einen String umwandelt. @Override public String toString() { return Integer.toString(value); } }