4. Vererbung - www2.inf.h

Werbung
4. Vererbung
Grundlagen der Vererbung
4. 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
String
String
int
boolean
String
void
String
void
boolean
void
98
4. Vererbung
Grundlagen der Vererbung
Wenn wir diese beiden Klassen realisieren, stellen wir fest, dass der Quelltext der
Klassen weitgehend identisch ist.
☞ siehe Beispiele
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
Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08
99
4. Vererbung
Grundlagen der Vererbung
Idee der Vererbung
• 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
Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08
100
4. Vererbung
Grundlagen der Vererbung
Medium
−titel: String
−spielzeit: int
−habIch: boolean
−kommentar: String
+void setzeKommentar(String)
+String gibKommentar()
+void setzeVorhanden(boolean)
+boolean gibVorhanden()
+void ausgeben()
CD
−kuenstler: String
−titelanzahl: int
Video
−regisseur: String
+String gibRegisseur()
+String gibKuenstler()
+int gibTitelanzahl()
Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08
101
4. Vererbung
Grundlagen der Vererbung
Terminologie
• 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
102
4. Vererbung
Grundlagen der Vererbung
Beispiel:
Eine
Vererbungshierarchie für graphische
Elemente.
Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08
103
4. Vererbung
Vererbung in Java
Vererbung in Java
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
104
4. Vererbung
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
105
4. Vererbung
Vererbung in Java
Vererbung und Zugriffsrechte
• 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.
Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08
106
4. Vererbung
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.
Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08
107
4. Vererbung
Vererbung in Java
Vererbung und Initialisierung
• Ü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?
• 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
108
4. Vererbung
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
109
4. Vererbung
Vererbung in Java
public class CD extends Medium {
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;
}
...
}
☞ 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
110
4. Vererbung
Vererbung in Java
Vorteile der Vererbung (bis hierher)
•
•
•
•
Vermeidung von Quelltext-Duplizierung
Wiederverwendung von Quelltext
Einfachere Wartung
Erweiterbarkeit
Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08
111
4. Vererbung
Subtyping
Subtyping und Ersetzbarkeit
• 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:
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
112
4. Vererbung
Subtyping
Beispiel: Die folgenden Zuweisungen sind zulässig:
Medium medium = new Medium(...);
Medium cd
= new CD(...);
Medium video = new Video(...);
• 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.
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
113
4. Vererbung
Subtyping
Dieses Prinzip der Ersetzbarkeit gilt natürlich auch bei einer Parameterübergabe.
public class MedienDatenbank {
...
public void einfuegen(Medium medium) {
...
}
}
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
114
4. Vererbung
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
115
4. Vererbung
Die Klasse Object
Die Klasse Object
• 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
Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08
116
4. Vererbung
Ü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
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
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
118
4. Vererbung
Ü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
119
4. Vererbung
Überschreiben von Methoden
Die Anweisung cd.ausgeben(); liefert jetzt:
Rolling Stones, 10 Titel
• 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
120
4. Vererbung
Ü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
Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08
121
4. Vererbung
Überschreiben von Methoden
• 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.
Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08
122
4. Vererbung
Überschreiben von Methoden
Vererbung und Methodenausführung
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
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
4. Vererbung
Überschreiben von Methoden
Methoden-Polymorphie:
• 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
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
4. Vererbung
Überschreiben von Methoden
Wir wollen die Frage, welche Instanzmethode bei einem Methodenaufruf aufgerufen
wird, wird noch etwas genauer betrachten.
Methodensuche:
• 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.
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
Herunterladen