4. Vererbung Grundlagen der Vererbung 4. Vererbung Grundlagen der Vererbung 4. Vererbung Idee der Vererbung Wir wollen ein Verwaltungsprogramm für CDs und Videos entwickeln. Wir stellen uns dazu folgende Klassen vor: CD titel: kuenstler: titelanzahl: spielzeit: habIch: kommentar: setzeKommentar(String) gibKommentar() setzeVorhanden(boolean) gibVorhanden() ausgeben() String String int int boolean String void String void boolean void Video titel: regisseur: spielzeit: habIch: kommentar: setzeKommentar(String) gibKommentar() setzeVorhanden(boolean) gibVorhanden() ausgeben() Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 4. Vererbung String String int boolean String void String void boolean void • Statt die beiden Klassen CD und Video unabhängig voneinander zu definieren, definieren wir zuerst eine Klasse, die die Gemeinsamkeiten von CD und Video zusammenfasst. Wir wollen diese Klasse Medium nennen. • Wir definieren dann anschließend, dass eine CD ein Medium ist und ebenso, dass ein Video ein Medium ist. • Schließlich ergänzen wir die Klassen für CD und Video ausschließlich um ihre spezifischen Eigenschaften. ☞ Prinzip der Generalisierung 98 Grundlagen der Vererbung Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 100 4. Vererbung Grundlagen der Vererbung Wenn wir diese beiden Klassen realisieren, stellen wir fest, dass der Quelltext der Klassen weitgehend identisch ist. Medium −titel: String −spielzeit: int −habIch: boolean −kommentar: String ☞ siehe Beispiele +void setzeKommentar(String) +String gibKommentar() +void setzeVorhanden(boolean) +boolean gibVorhanden() +void ausgeben() Nachteile dieses Ansatzes: • erhöhter Aufwand der Erstellung • bei der Wartung von dupliziertem Code sind Änderungen an mehreren Stellen notwendig • Wartung ist fehleranfällig • erhöhter Testaufwand • erhöhter Aufwand bei der Nutzung der Klassen CD −kuenstler: String −titelanzahl: int Video −regisseur: String +String gibRegisseur() +String gibKuenstler() +int gibTitelanzahl() Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 99 Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 101 4. Vererbung Grundlagen der Vererbung 4. Vererbung Terminologie Vererbung in Java • Eine Superklasse (Oberklasse) ist eine Klasse, die von anderen Klassen erweitert wird. • Eine Subklasse (Unterklasse) ist eine Klasse, die eine andere Klasse erweitert. Man sagt auch, dass die Subklasse von der Superklasse erbt. • Vererbung bedeutet, dass die Subklasse alle Datenfelder und Methoden von der Superklasse übernimmt. • Es werden keine Konstruktoren vererbt! • Klassen, die über eine Vererbungsbeziehung miteinander verknüpft sind, bilden eine Vererbungshierarchie. Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 4. Vererbung Vererbung in Java 102 Grundlagen der Vererbung Beispiel: Eine Vererbungshierarchie für graphische Elemente. Syntax: public class Unterklasse extends Oberklasse { ... } • Mit Hilfe von extends wird die Oberklasse zu einer Klasse angegeben. • Java unterstützt ausschließlich einfache Vererbung, d.h. wir können höchstens eine Oberklasse angeben. • Ohne Verwendung von extends ist implizit die Klasse java.lang.Object die Oberklasse. Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 4. Vererbung 104 Vererbung in Java Definition der Oberklasse Medium: Definition der Unterklassen: public class Medium { private String titel; private int spielzeit; private boolean habIch; private String kommentar; ... } public class CD extends Medium { private String kuenstler; private int titelanzahl; ... } public class Video extends Medium { private String regisseur; ... } ☞ In den Unterklassen werden zusätzliche Datenfelder und Methoden deklariert. ☞ Darüberhinaus verfügt ein Objekt der Unterklasse über alle Datenfelder und Methoden der Oberklasse. Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 103 Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 105 4. Vererbung Vererbung in Java 4. Vererbung Vererbung in Java Vererbung und Zugriffsrechte Vererbung und Initialisierung • Von einer Unterklasse aus kann man nicht auf die private deklarierten Datenfelder und Methoden der Oberklasse zugreifen. • Daher gibt es zusätzlich den Modifikator (Zugriffsrecht) protected. • Das Zugriffsrecht protected – erlaubt den Zugriff von Unterklassen aus, – erlaubt den Zugriff für Klassen des gleichen Pakets und – verbietet den Zugriff für alle anderen Klassen. • Der Modifikator protected kann nur auf Datenfelder, Konstruktoren und Methoden angewendet werden, nicht auf Klassen. • Datenfelder werden üblicherweise nicht als protected deklariert, da dies die Kapselung schwächen würde. Stattdessen definiert man Zugriffsmethoden die protected oder public sind. • Typischer Einsatz von protected: Bei Hilfsmethoden, die nach außen verborgen werden sollen, für Unterklassen aber hilfreich sein können. • Üblicherweise sorgt ein Konstruktor dafür, dass die Datenfelder eines Objekts nach der Erzeugung in einem vernüftigem Zustand sind. • Wie ist das nun, wenn die Datenfelder durch Vererbung über verschiedene Klassen verteilt sind? Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 4. Vererbung 106 Vererbung in Java • Der Einsatz von protected ist nur über Paketgrenzen hinweg sinnvoll. • protected wird deutlich seltener als public und private eingesetzt. • Bevor Sie protected verwenden, sollten Sie kritisch prüfen, ob private nicht auch ausreicht. • Man beachte: protected ist weniger restriktiv als die Verwendung keines Modifikators. • Unter- und Oberklasse bieten Konstruktoren an. • Die Unterklasse kümmert sich in ihrem Konstruktor nur um die Datenfelder, die in der Unterklasse definiert sind. • Damit auch die Datenfelder der Oberklasse korrekt initialisiert werden, rufen wir den Konstruktor der Oberklasse auf. • Der Aufruf des Konstruktors der Oberklasse erfolgt mit dem Schlüsselwort super. • Dieser Aufruf muss stets die erste Anweisung in einem Konstruktor der Unterklasse sein! Ansonsten fügt der Compiler den Aufruf eines parameterlosen Konstruktors für die Oberklasse ein. Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 4. Vererbung 108 Vererbung in Java public class Medium { private private private private String titel; int spielzeit; boolean habIch; String kommentar; public Medium(String titel, int spielzeit) { this.title = titel; this.spielzeit = spielzeit; this.habIch = false; this.kommentar = ""; } ... } Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 107 Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 109 4. Vererbung Vererbung in Java 4. Vererbung public class CD extends Medium { Subtyping Subtyping und Ersetzbarkeit private String kuenstler; private int titelanzahl; public CD(String titel, String kuenstler, int stuecke, int spielzeit) { super(title,spielzeit); this.kuenstler = kuenstler; this.titelanahl = stuecke; } • Wir wissen: Klassen definieren Datentypen. • Wir haben gelernt: Klassen bilden eine Klassenhierarchie. • Analog zur Klassenhierarchie entsteht somit auch eine Typhierarchie mit Suptypen (Untertypen) und Supertypen (Obertypen). • Der Typ, der durch eine Subklasse definiert wird, ist ein Subtyp des Typs, der durch die Zugeordnete Superklasse definiert wird. ... Prinzip der Ersetzbarkeit: } ☞ Sie sollten den Konstruktor der Oberklasse auch dann explizit aufrufen, wenn der Compiler diesen Aufruf automatisch einfügen würde. Es ist guter Programmierstil. Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 4. Vererbung 110 Vererbung in Java Vorteile der Vererbung (bis hierher) • • • • Objekte von Subtypen können an allen Stellen verwendet werden, an denen ein Supertyp erwartet wird. Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 4. Vererbung 112 Subtyping Beispiel: Die folgenden Zuweisungen sind zulässig: Medium medium = new Medium(...); Medium cd = new CD(...); Medium video = new Video(...); Vermeidung von Quelltext-Duplizierung Wiederverwendung von Quelltext • Eine Variable kann Objekte halten, deren Typ entweder gleich dem deklarierten Typ der Variablen ist oder ein beliebiger Subtyp des deklarierten Typs ist. • Bei der Zuweisung wird eine implizite Typumwandlung (Cast) durchgeführt. Einfachere Wartung Erweiterbarkeit Umgekehrt gilt dies nicht. Die folgenden Anweisungen sind nicht zulässig: CD cd = new Medium(...); Video video = new CD(...); Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 111 Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 113 4. Vererbung Subtyping 4. Vererbung Dieses Prinzip der Ersetzbarkeit gilt natürlich auch bei einer Parameterübergabe. Die Klasse Object Die Klasse Object public class MedienDatenbank { ... public void einfuegen(Medium medium) { ... } } • Alle Klassen ohne explizit deklarierte Superklasse haben die Klasse Object als Superklasse. • Object gehört zum Paket java.lang. • Object verfügt über einige vordefinierte Methoden. Diese stehen somit allen Objekten zur Verfügung. • Es kann sinnvoll sein, diese Methoden in Unterklassen zu überschreiben, d.h. mit einer anderen Implementierung zu versehen. • Beispiel: toString() • genaueres zu den Methoden: siehe API-Dokumentation Wir können diese Methode sowohl für Videos als auch für CDs verwenden. MedienDatenbank db = new MedienDatenbank(...); Video video = new Video(...); CD cd = new CD(...); db.einfuegen(video); db.einfuegen(cd); Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 4. Vererbung 114 Subtyping • Variablen für Objekte sind in Java polymorphe Variablen. • Der Ausdruck polymorph (vielgestaltig) bezieht sich auf die Tatsache, dass eine Variable Objekte verschiedener Typen referenzieren kann. • Polymorphie ist einer der Grundpfeiler der Objektorientierung. • Polymorphie tritt in objektorientierten Sprachen in unterschiedlichen Zusammenhängen auf. Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 4. Vererbung 116 Überschreiben von Methoden Überschreiben von Methoden Die Methode ausgeben() in der Klasse Medium können wir folgendermaßen definieren: public void ausgeben() { System.out.print(titel + " (" + spielzeit + "Min)"); if (habIch) System.out.println("*"); else System.out.println(); System.out.println(" " + kommentar); } Jetzt erzeugen wir eine Instanz der Klasse CD: CD cd = new CD("Beggar\’s Banquet", "Rolling Stones", 10, 41); Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 115 Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 117 4. Vererbung Überschreiben von Methoden Mit weiteren Anweisungen können wir einen Kommentar angeben und das wir im Besitz der CD ist. Die Anweisung 4. Vererbung Überschreiben von Methoden Die Anweisung cd.ausgeben(); liefert jetzt: Rolling Stones, 10 Titel cd.ausgeben(); erzeugt dann die Ausgabe Beggar’s Banquet (41 Min)* Die letzte Platte der Ur-Stones, ein absolutes Meisterwerk! Problem: • Die Ausgabe ist unzureichend. • Es werden nur die allgemeinen Informationen angezeigt, die in der Klasse Medium definiert sind. • Dedizierte CD-Informationen (Künstler, Titelanzahl) fehlen in der Ausgabe. Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 4. Vererbung 118 Überschreiben von Methoden • Um eine vernüftige Ausgabe für CDs zu erhalten, müssen wir die Methode ausgeben() überschreiben. • Eine Subklasse kann die Implementierung einer Methode überschreiben. • Dazu deklariert die Subklasse eine Methode mit der gleichen Signatur wie in der Superklasse, implementiert diese jedoch mit einem anderen Rumpf. • Bei Aufrufen an Objekte der Unterklasse wird die überschreibende Methode ausgeführt. Ein erster Versuch: public class CD extends Medium { ... public void ausgeben() { System.out.println( " " + kuenstler + ", " + titelanzahl + " Titel"); } } Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 • Jetzt geben wir nur die CD-spezifischen Information aus, es fehlen die allgemeinen Informationen. • Auf die Instanzvariablen von Medium haben wir aber innerhalb von CD keinen Zugriff. Sollte man diese protected deklarieren? • Nein, u.U. können wir dies auch gar nicht, da wir nicht den Quelltext von Medium besitzen. • Stattdessen nutzen wir super, um innerhalb der überschreibenden Methode, die Methode der Oberklasse aufzurufen. Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 4. Vererbung 120 Überschreiben von Methoden Ein zweiter Versuch: public class CD extends Medium { ... public void ausgeben() { System.out.print("CD: " + kuenstler + ": "); super.ausgeben(); System.out.println( " " + titelanzahl + " Titel"); } } Jetzt liefert die Anweisung cd.ausgeben();: CD: Rolling Stones: Beggar’s Banquet (41 Min)* Die letzte Platte der Ur-Stones, ein absolutes Meisterwerk! 10 Titel 119 Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 121 4. Vererbung Überschreiben von Methoden 4. Vererbung Überschreiben von Methoden Methoden-Polymorphie: • Ein super-Aufruf in einer Methode hat immer folgende Form: super.Methodenname( Parameterliste ); Der Methodenname muss also explizit genannt werden. • Im Gegensatz zu super-Aufrufen in Konstruktoren kann der super-Aufruf in einer Methode an jeder beliebigen Stelle der Methode erfolgen. • Es muss auch kein super-Aufruf erfolgen und wenn dieser nicht erfolgt, wird auch nicht automatisch ein solcher Aufruf generiert. • Methodenaufrufe in Java sind polymorph. • Derselbe Methodenaufruf kann zu unterschiedlichen Zeitpunkten verschiedene Methoden aufrufen. • Welche Methode tatsächlich aufgerufen wird, ist abhängig vom dynamischen Typ der Variablen, mit der der Aufruf durchgeführt wird. Weiteres Beispiel: • Wir gehen davon aus, daß die Klassen CD und Video die Methode ausgeben() aus Medium überschreiben. • Dann wird für i==0 die Methode ausgeben() von CD aufgerufen und • für i==1 die Methode ausgeben() von Video. Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 4. Vererbung 122 Überschreiben von Methoden Vererbung und Methodenausführung Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 4. Vererbung Medium m; Medium m1 = new CD(...); Medium m2 = new Video(...); for (int i=0 ; i<2 ; i++) { if (i==0) m = m1; else m = m2; m.ausgeben(); } 124 Überschreiben von Methoden Wir wollen die Frage, welche Instanzmethode bei einem Methodenaufruf aufgerufen wird, wird noch etwas genauer betrachten. Methodensuche: Medium medium = new CD("Beggar\’s Banquet", "Rolling Stones", 10, 41); ... medium.ausgeben(); • Welche Methode ausgeben() wird hier ausgeführt? Die der Klasse Medium, weil die Variable medium von diesem Typ ist? ☞ statischer Typ der Variablen medium • Beim Aufruf einer Instanzmethode findet eine sogenannte Methodensuche statt. • Ausgehend vom dynamischen Typ der Variablen wird in der Vererbungshierarchie nach einer Methode gesucht, die auf die Signatur des Aufrufs passt. • Wenn die Klasse des dynamischen Typs keine solche Methode aufweist, wird in der direkten Oberklasse gesucht, usw. • Die Suche endet spätestens in der Klasse Object. Die der Klasse CD, weil das Objekt, auf dem ausgeben() aufgerufen wird, eine Instanz von CD ist? ☞ dynamischer Typ der Variablen medium ☞ Entscheidend für die Methodenauswahl ist der dynamische Typ einer Variablen. Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 123 Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 125 4. Vererbung Überschreiben von Methoden Die Methode toString() • Jedes Objekt hat eine Methode toString(). • Diese Methode ist in der Klasse Object definiert. • toString() wird implizit aufgerufen, wenn eine Objektreferenz als Parameter der Methode println() verwendet wird. MeineKlasse obj = new MeineKlasse(); System.out.println(obj); • Die Implementierung in Object liefert einen String der Art: Klassenname@Referenz • Hier bietet es sich an, in Unterklassen die Methode toString zu überschreiben. Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 126