Zusatzmaterial DIE KLASSE OBJECT Techniken der Programmentwicklung Prof. Dr. Wolfgang Schramm Object – wich7ge Eigenscha<en der Klasse 1 o Object ist die Wurzel jeder Vererbungshierarchie. ¤ ⇒ o o Object enthält einige o< verwendete Methoden, die an alle anderen Klassen vererbt werden. Die (zum jetzigen Zeitpunkt) wich7gsten Methoden von Object: ¤ public Class getClass()! ¤ ¤ public boolean equals(Object o)! public int hashCode()! public String toString()! ¤ protected Object clone()! ¤ o Jede Java-­‐Klasse erbt direkt oder indirekt von Object. Eine Variable vom Typ Object kann Objekte jeden beliebigen Typs aufnehmen. Weitere wich7ge Methoden: ⟶ Threads. getClass – Zweck und Arbeitsweise 2 o o Java – Klassen sind Objekte vom Typ java.lang.Class. getClass() liefert die Referenz auf das Klassenobjekt, welches das Objekt konstruiert hat. ¤ ¤ o Das Class-­‐Objekt ist immer eindeu7g in der JVM, so dass auch mehrere Anfragen an getClass() immer dasselbe Class-­‐Objekt liefern. Die Objektmethode getName() eines Class-­‐Objekts liefert den Namen der Klasse. Class-­‐Objekte spielen insbesondere bei dynamischen Abfragen über die sogenannte Reflec7on eine Rolle. ¤ Zur Laufzeit können so beliebige Klassen geladen, Objekte erzeugt und Methoden aufgerufen werden. Vergleich von Objekten 3 o o o o o o Wie vergleicht man 2 Objekte miteinander? == -­‐ Operator ? Wieso passt der meistens nicht? Welche Alterna7ven gibt es? Um ein korrektes (gewünschtes) Vergleichsergebnis zu erzielen → eigene Vergleichsmethode (equals) schreiben. In der Klasse Object gibt es bereits eine Methode equals. Die Methode equals -­‐ Wie arbeitet sie? 4 o o o o o o Eine equals()-Methode soll Objekte auf Gleichheit prüfen. boolean equals(Object o) testet, ob das andere Objekt mit dem eigenen logisch gleich ist. Die Gleichheit definiert jede Klasse für sich anders. Die Standardimplemen7erung aus der Klasse Object kann über die Gleichheit von speziellen Objekten nichts wissen → testet die Referenzen. Unterklassen der Klasse Object überschreiben diese Methode → inhaltlicher (logischer) Vergleich der Objekte. Jede Klasse benö7gt eine unterschiedliche Logik, um festzulegen, wann ein Objekt mit einem anderen Objekt logisch gleich ist. Die Methode equals -­‐ Wann muss man sie überschreiben? 5 o Wenn eine Klasse die logische Gleichheit kennt, die sich von der reinen Gleichheit (Iden7tät) der Objekte unterscheidet. ¤ o ! Beispiel: Wertklassen Integer, Date! Wenn ein Programmierer equals für 2 Referenzen auf Objekte verwendet, dann will er nicht wissen, ob sie dasselbe Objekt referenzieren, sondern ob sie logisch äquivalent sind. equals -­‐ Vertrag für die Arbeitsweise der Methode 6 equals implemen7ert eine Äquivalenzrela7on, mit folgenden Eigenscha<en: o Reflexivität: Für jeden Referenzwert x gilt: x.equals(x) = true. o Symmetrie: Für die Referenzwerte x und y gilt: x.equals(y) = y.equals(x). o Transi1vität: Für die Referenzwerte x, y und z gilt: Wenn x.equals(y) = true ∧ y.equals(z) = true ⇒ x.equals(z) = true. o Konsistenz: Für die Referenzwerte x und y gilt, dass x.equals(y) bei mehrfachen Aufrufen immer dasselbe Ergebnis liefert – entweder true oder false, vorausgesetzt, dass keine von x und y in equals-­‐Vergleichen verwendete Informa7on verändert wurde. o Für jeden Referenzwert x gilt x.equals(null) = false. [Spezifika7on von java.lang.Object] Anmerkung: Die Referenzwerte x, y und z sind ≠ null. equals – Implemen7erung 1/5 7 o o Nicht jede Klasse implemen7ert eine eigene equals()-­‐ Methode, so dass die Laufzeitumgebung unter Umständen ungewollt bei Object und seinem Referenzenvergleich landet. Wie sieht es aus bei größeren Klassenhierarchien? Beispiel: class Base { … } class Inh extends Base { … } Wie muss die equals-­‐Methode aussehen? equals – Implemen7erung 2/5 8 Warum (nicht) so? class Base { Falsche Signatur protected int val; Methode ist überladen statt überschrieben! ... public boolean equals (Base o) { return this.val == o.val; } } Also so? class Base { protected int val; ... public boolean equals (Object o) { return this.val == o.val; } } Was ist falsch? equals – Implemen7erung 3/5 9 Also so? class Base { protected int val; ... public boolean equals (Object o) { Base that = (Base) o; return this.val == that.val; } } Was ist immer noch falsch? Also so? class Base { protected int val; ... public boolean equals (Object o) { Base that = (Base) o; if (o == null) return false; else return this.val == that.val; } } Was ist immer noch falsch? equals – Implemen7erung 4/5 10 Also so? class Base { protected int val; ... public boolean equals (Object o) { if (!(o instanceof Base)) return false; Base that = (Base) o; Asymmetrisch! if (o == null) return false; else return this.val == that.val; } Was gibt’s jetzt noch zu meckern? } class Inh extends Base { public boolean equals (Object o) { . . . if ( !(o instanceof Inh)) return false; else return ((Inh)o).val == val; } equals – Implemen7erung 5/5 11 Base bo = new Base(7); Inh io = new Inh (7); println ("bo = io: " + bo.equals(io)); println ("bo = io: " + io.equals(bo)); è Probleme mit der Symmetrie! è Typgleichheit überprüfen ! Nur Objekte derselben Klasse können verglichen werden if ( ! ( o.getClass().equals(getClass())) ) . . . Verletzung des Liskov‘sches SubsGtuGonsprinzips: . Alle Methoden der Oberklasse können auf Objekte der Unterklasse angewendet werden. è Besser: Keine Unterklasse bilden, sondern (bisherige) Basisklasse als Komponente in der neuen Klasse. hashCode – Zweck und Arbeitsweise 12 o o o o Die Bedeutung der Hash-­‐Funk7on bzw. des Hash-­‐Codes (⟶ ADS): Die Hash-­‐Funk7on (auch Streuwerounk7on) erzeugt zu einer Eingabe (aus einer üblicherweise großen Quellmenge) eine Ausgabe, den Hashcode (meist aus einer kleineren Zielmenge). Also hashCode () in Java = Hash-­‐Funk7on zur Berechnung des Hash-­‐Codes: Dient dazu, einen möglichst eindeu7gen Hash-­‐Wert (32 Bit int-­‐Wert) für das Objekt zu erzeugen. Die Implemen7erung von Object verwendet einen einmaligen Schlüssel für den Hash-­‐Wert, unabhängig von den Daten im Objekt. Man überschreibt hashCode(), um einen Hash-­‐Wert aus den Daten des Objekts zu berechnen. hashCode-­‐ Vertrag für die Arbeitsweise der Methode 13 Die hashCode-­‐Methode implemen7ert eine Rela7on, mit folgenden Eigenscha<en: o Konsistenz: Für den Referenzwert x gilt, dass x.hashCode() bei mehrfachen Aufrufen immer denselben int-­‐Wert liefert – vorausgesetzt, dass keine von x verwendete Informa7on verändert wurde. o Wenn für zwei Referenzwerte x und y gilt: ¤ x.equals(y) = true ⇒ x.hashCode() = y.hashCode(). ¤ x.equals(y) = false ⇏ x.hashCode() != y.hashCode. Anmerkung: Die Referenzwerte x, y und z sind ≠ null. hashCode-­‐ Vertrag für die Arbeitsweise der Methode 14 o Wenn man equals() überschreibt, so muss man auch hashCode() überschreiben und umgekehrt: ¤ ¤ ¤ Sowohl equals() als auch hashCode() müssen dieselben Felder des Objektes einbeziehen, da andernfalls der Vertrag von equals bzw. hashCode nicht für alle möglichen Wertekombina7onen der Felder gewährleistet werden kann. Zwei inhaltlich gleiche Objekte müssen denselben Hashcode haben. Haben zwei Objekte denselben Hashcode können sie inhaltlich durchaus unterschiedlich sein. toString -­‐ Zweck und Arbeitsweise 15 o o o o o Liefert eine textuelle Darstellung des Objekts als String zurück. Idee: kurze informa7ve Darstellung, soll dem Leser (z.B. beim Debuggen) helfen. Rückgabe der Standardimplemen7erung: Klassenname@Hexdezimalwert des Hashcodes des Objekts. Wird automa7sch bei String-­‐Verknüpfungen aufgerufen. Dient nicht der Serialisierung von Objekten. 2 Möglichkeiten zum Replizieren eines Objekts 16 o o Copy-­‐Konstruktor – der nimmt ein vorhandenes Objekt als Vorlage, erzeugt ein neues Objekt und kopiert den Zustand des vorhandenen Objekts in das neue Objekt. Bereitstellen einer öffentlichen Methode clone. ¤ Dafür: Rückgriff auf die protected Methode clone der Klasse Object. clone (aus Object) -­‐ Zweck und Arbeitsweise 17 o o o o Kopiert Objekte. Erzeugt eine flache Kopie des Objekts (shallow copy). Kein Aufruf von Konstruktoren zur Erzeugung der Kopie. Anwendung (in eigenen Klassen) ¤ ¤ ¤ ¤ Die protected clone()-­‐Methode muss überschrieben werden. Klasse muss Cloneable implemen7eren. In der ersten Zeile muss super.clone() aufgerufen werden. Tiefe Kopien (deep copy) werden durch rekursiven Aufruf von clone() auf den Datenelementen (die müssen clone dann ebesfalls überschreiben!) erzeugt. Eigene clone-­‐Methode 18 o o Eigene Klassen überschreiben die protected-­‐Methode clone() aus der Oberklasse Object und machen sie public. Wie implemen7eren? ¤ ¤ ¤ Von Hand ein neues Objekt anlegen, alle Atribute kopieren und die Referenz auf das neue Objekt zurückgeben. à ADS Das Laufzeitsystem soll selbst eine Kopie anlegen und diese zurückgeben. Lösung zwei verkürzt die Entwicklungszeit und ist schwerer zu verstehen. Eigene clone-­‐Methode -­‐ Details 19 o o Klonen durch das System in 2 Schriten: Der Aufruf super.clone() ru< Methode clone() aus Object auf und veranlasst so die Laufzeitumgebung, ein neues Objekt zu bilden ¤ ¤ ¤ ¤ o kopiert die nicht-­‐sta7schen Atribute, kopiert elementweise die Daten des aktuellen Objekts in das neue. clone ist Object protected, aber das ist der Trick: Nur Unterklassen können clone() aufrufen, keiner sonst. clone() gibt eine Referenz auf das neue Objekt zurück; gibt es keinen freien Speicher mehr à OutOfMemoryError. Die Klasse implemen7ert die Markierungsschnitstelle Cloneable. ¤ ¤ Falls von außen ein clone() auf einem Objekt aufgerufen wird, dessen Klasse nicht Cloneable implemen7ert à CloneNotSupportedExcep7on. Object implemen7ert die Schnitstelle Cloneable nicht selbst, denn sonst häten ja Klassen schon automa7sch diesen Typ, was sinnlos wäre. clone und equals 20 o o Die Methode clone() und die Methode equals() hängen, wie auch equals() und hashCode(), miteinander zusammen. Wenn die clone()-­‐Methode überschrieben wird, sollte auch equals() angepasst werden, denn ohne ein überschriebenes equals() bleibt Folgendes in Object stehen: public boolean equals( Object obj ) { return (this == obj); } o o o Das bedeutet aber: o.clone().equals(o) == false. Diese Seman7k ist nicht erwünscht. Deshalb: wenn clone überschrieben à equals überschreiben.