Kapitel 17: Objektorientierung in JavaGrundlagen der Programmierung 1
Holger Karl
Wintersemester 2016/2017
Inhaltsverzeichnis
Inhaltsverzeichnis 1
Abbildungsverzeichnis 2
Liste von Definitionen u.ä. 217.1 Überblick . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 317.2 Klassen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 317.3 Methoden . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 717.4 Einfachvererbung . . . . . . . . . . . . . . . . . . . . . . . . . . 1817.5 Mehrfachvererbung und Ersatz dafür . . . . . . . . . . . . . . . 2817.6 Zusammenfassung, Einordnung . . . . . . . . . . . . . . . . . . 38
1
Abbildungsverzeichnis
17.1 Methodenaufruf mit Referenzparameter: Vor Zuweisung in f . 1117.2 Methodenaufruf mit Referenzparameter: Nach Zuweisung in f 1117.3 Methodenaufruf mit Referenzparameter: Vor Zuweisung in g . 1217.4 Methodenaufruf mit Referenzparameter: Nach Zuweisung in g 1217.5 Nach Rückkehr aus g . . . . . . . . . . . . . . . . . . . . . . . 1317.6 Entwickler sind mit Mehrfachvererbung überfordert . . . . . . . 2817.7 UML-Diagramm einer einfachen Tierfarm . . . . . . . . . . . . 2917.8 Unterschied zwischen abstract class und interface . . . . . . . . 3617.9 defaults in interfaces . . . . . . . . . . . . . . . . . . . . . . . . 36
Liste von Definitionen u.ä.
17.1 Definition (Signatur einer Methode) . . . . . . . . . . . . . . . 817.2 Definition (Sichtbarkeit einer Variablen in Java) . . . . . . . . . 1617.3 Definition (Lebensdauer einer Variable in Java) . . . . . . . . . 16
2
17.1. Überblick 3
17.1 Überblick
17.1.1 Was bisher geschah
• Im vorherigenKapitel habenwir uns dieGrundlagen von Java angeschaut– Typisierte Variablendeklaration– Art von Typen: primitive und reference– Kontrollstrukturen– Syntax
17.1.2 Dieses Kapitel
Objektorientierung in Java
• Anders strukturiertes Objektmodell– Mistrauen-basiert
• Insbes. Vererbung funktioniert anders• Erfordert komplizierte Umgehungskonstruktionen wie Interfaces undabstrakte Klassen
17.2 Klassen
17.2.1 Objektorientierung
• Auf ersten Blick ist OO in Java nicht viel anders als in Python• Gemeinsamkeiten:
– Es gibt Klassen und Objekte– Klassen haben Attribute: Daten und Methoden
• Wenig überraschende Unterschiede– Daten müssen mit Typ deklariert werden!– Methoden: Parameter und Rückgabewert haben Typ
• Unwichtige Unterschiede: Syntaxdetails, z.B. new• Wesentliche Unterschiede: Das Vererbungskonzept!
17.2.2 Klassen: Deklaration eines Typs
• Deklaration einer Klasse: class– Erschafft einen neuen Datentyp
• Wesentlicher Punkt: Die Daten der Klassen werden deklariert, nicht nurdie Methoden
4 Liste von Definitionen u.ä.
1 class Klassenname {2 Variablendeklaration1;3 Variablendeklaration2;4 Variablendeklaration3;5
6 Methodendeklaration17 Methodendeklaration28 Methodendeklaration39 }
17.2.3 Klassendeklaration: Beispiel Ellipse
Zunächst eine Klasse ohne Methode
• Im Block der Klasse die Deklarationen der Daten-Attribute eines Ellipse-Objektes
1 class Ellipse {2 // Daten deklarieren:3 double x, y;4 double achse1, achse2;5 }
17.2.4 Ellipsenobjekt erzeugen
Zwei Aspekte: Deklaration und Erzeugung
Deklaration
Wir deklarieren eine Variable vom Typ Ellipse
• Analogie: eine leere Schachtel anlegen• Syntax entspricht Deklaration von int, String, etc. – Ellipse ist einneuer Typ
1 // Variable deklarieren; es gibt noch kein Objekt!2 Ellipse e;
17.2.5 Ellipsenobjekt erzeugen
Zwei Aspekte: Deklaration und Erzeugung
17.2. Klassen 5
Erzeugung
• Wir erzeugen ein Objekt vom Typ Ellipse– Schlüsselwort: new
* Mit Aufruf des Konstruktors (ggf. samt Parameter)– Analogie: Wir erzeugen Inhalt
• Wir lassen die deklarierte Variable darauf referenzieren– Durch eine Zuweisung– Analogie: Wir packen den Inhalt in die Schachtel
1 // Variable deklarieren; es gibt noch kein Objekt!2 Ellipse e;3
4 // Objekt erzeugen: Schlüsselwort new5 // aber das Objekt geht sofort verloren;6 // wir müssen eine Referenz darauf aufheben7 new Ellipse();8
9 // Also richtig:10 e = new Ellipse();
Anmerkung: Trennung von Deklaration und Erzeugung in Python?
Dieser zweischrittige Prozess kommt Ihnen – aus der Python-Perspektive –sicherlich seltsam vor. Dort werden Variablen nicht deklariert (in einer dyna-misch typisierten Sprache nicht notwendig); sie werden bei Bedarf imMomentder Zuweisung erschaffen.
17.2.6 Zugriff auf Daten
Auf Daten eines Objekts kann wie gewohnt durch Punkt-Notation zugegriffenwerden
1 class Ellipse {2 // Daten deklarieren:3 double x, y;4 double achse1, achse2;5 }6
7 Ellipse e;8 e = new Ellipse();9 e.x = 17.12;10 System.out.println(2 * e.x);
6 Liste von Definitionen u.ä.
e ==> nulle ==> Ellipse@6e1567f1$4 ==> 17.1234.24
17.2.7 Zuweisung zwischen Objektvariablen
Variablen vom Typ einer Klasse sind Referenzvariablen!
• Analog zu Array, String• Also Zuweisung: Kopie der Referenz!
1 class Ellipse {2 // Daten deklarieren:3 double x, y;4 double achse1, achse2;5 }6
7 Ellipse e1, e2;8 e1 = new Ellipse();9 e2 = e1;10 e1.x = 17.12;11 System.out.println(e2.x);
e1 ==> nulle2 ==> nulle1 ==> Ellipse@56ef9176e2 ==> Ellipse@56ef9176$6 ==> 17.1217.12
17.2.8 Arrays in Objekten, Arrays von Objekten
• Daten einer Klasse dürfen beliebigen Typ haben, also auch Arrays• Beispiel
1 class Zeile {2 int[] zahlen;3 }
17.3. Methoden 7
4
5 Zeile[] matrix;
17.3 Methoden
17.3.1 Methoden einer Klasse
• Methoden-Attribute ähnlich zu Python def in Klasse• Syntax:
– Typ des Rückgabewerts der Methode* Ggf. spezieller Typ void falls keine Rückgabe
– Name der Methode– Parameterliste (auch leer)
* Mit Typen* Kein self als formaler Parameter nötig!
– Rumpf der Methode, in geschweiften Klammern als Block• In Methodenrumpf
– this entspricht self (Schlüsselwort) – implizit statt explizit vor-handen
• Aufruf: Punktnotation
17.3.2 Klassendeklaration: Beispiel Ellipse mit Methode
1 class Ellipse {2 // Daten deklarieren:3 double x, y;4 double achse1, achse2;5
6 void verschiebe(double deltax, double deltay) {7 this.x += deltax;8 this.y += deltay;9 }10 }11
12 // verkürzte Notation:13 Ellipse e = new Ellipse();14 e.x = 17.12;15 e.verschiebe(5, -0.2);16 System.out.println(e.x);
8 Liste von Definitionen u.ä.
e ==> Ellipse@cb644e$3 ==> 17.1222.12
17.3.3 Signatur einer Methode
Definition 17.1 (Signatur einer Methode). Die Signatur einer Methode istfestgelegt durch
• den Namen der Methode• die Anzahl und Typen der Parameter.
Anmerkung
Der Typ des Rückgabewerts (ggf. void) wird in Java nicht als Teil der Signaturangesehen Signatur in Java. Es gibt Programmiersprachen, bei denen auchdas als Teil der Signatur aufgefasst wird.
17.3.4 Überladene Methoden
• Zwei Methoden der gleichen Klasse müssen unterschiedliche Signaturhaben
• D.h., es kann zwei Methoden gleichen Namens aber unterschiedlicherParametertypen/-anzahl in einer Klasse geben!
• Solche Methoden heißen überladene Methoden– Präziser: DerName der Methode ist überladen; aber hier wird meistschlampig formuliert
Aufruf überladener Methoden?
Wie wird beim Aufruf die richtige Methode ausgesucht?
• Die Typen der Aufrufparameter sind entscheident• Ggf. nach impliziten Typecasts
– Wähle Methode, die am nächsten bei den Typen des Aufrufs liegt
17.3.5 Überladene Methoden: Beispiele
• System.out.print!
1 class UeberladeneMethoden {2 void f(int a) { System.out.println("Variante 1"); }3 void f(char a) { System.out.println("Variante 2"); }4 void f(String a) { System.out.println("Variante 3"); }5 void f(double a) { System.out.println("Variante 4"); }
17.3. Methoden 9
6 }7
8 UeberladeneMethoden u = new UeberladeneMethoden();9
10 u.f(’a’);11 u.f(17);12 u.f(4096);13 u.f("Hallo");14 u.f(42.17);
u ==> UeberladeneMethoden@cb644eVariante 2Variante 1Variante 1Variante 3Variante 4
17.3.6 Aufrufsemantik: Parameter
Parameter ein primitive type
• Aktueller Wert wird dem formalen Parameter zugewiesen– Also: Kopiert!
• Veränderung des formalen Parameters schlägt sich nicht beim Aufrufernieder
Parameter ein reference type
• Die Referenz wird dem formalen Parameter zugewiesen– Nicht das referenzierte Objekt!– Dabei entsteht keine Kopie!
• Veränderung des formalen Parameters schlägt sich nicht beim Aufrufernieder
• Wohl aber eine Veränderung des referenzierten Objekts!
17.3.7 Aufrufsemantik: Beispiel für Aufruf mit Referenztypen
1 public class CallSemantics{2 public static void f(String [] x) {3 x[1] = "Neuer String";4 }5
6 public static void g(String [] x) {7 x = new String[] {"lokaler", "Wert"};
10 Liste von Definitionen u.ä.
8 }9
10 public static void main(String [] args) {11 String [] s = new String[2];12 s[0]= "Hallo";13 s[1] = "GP1";14 System.out.println(s[1]);15
16 f(s);17 System.out.println(s[1]);18
19 g(s);20 System.out.println(s[1]);21 }22 }
GP1Neuer StringNeuer String
(PT link)
Visualisierung
Um das Verhalten bei unterschiedlicher Nutzung eines Referenz-Parametersdarzustellen, hier einige Illustrationen zu diesem Code-Beispiel.
1. Vor der Zuweisung in fBetrachten wir den Zustand in Abbildung 17.1. Methode f wurde aufge-rufen. Der Parameter x ist eine Referenz auf das gleiche Array, das inmain angelegt wurde und dort mit der Variable s referenziert wird.
2. Nach der Zuweisung in fAbbildung 17.2 ist der Zustand nach der Zuweisung an x[1] in Methodef. Die beiden Variablen s und x zeigen immer noch auf das gleicheArray-Objekt; bei diesem Array wurde der zweite Eintrag verändert.Diese Änderung ist dann auch in main unter s sichtbar und wird dannauch entsprechend ausgegeben.
3. Vor der Zuweisung in gDer Zustand vor der Zuweisung in g ist in Abbildung 17.3 zu sehen.Hier besteht zunächst kein Unterschied zur Situation beim Aufruf derMethode f.
4. Nach der Zuweisung in gDer interessante Teil ist natürlich nach der Zuweisung in g (Abbil-dung 17.4). Der entscheidende Punkt ist, dass hier an die Variable x
17.3. Methoden 11
Abbildung 17.1: Methodenaufruf mit Referenzparameter: Vor Zuweisung in f
Abbildung 17.2: Methodenaufruf mit Referenzparameter: Nach Zuweisung inf
selbst die Zuweisung erfolgt (und nicht an eine Komponente des Arrays).Dadurch zeigt x jetzt auf ein neues Array, das mit dem Array das durchs referenziert wird, nichts zu hat! Entsprechend verschwinden dieseWerte nach der Rückkehr von g.
5. Nach Rückkehr aus gAbbildung 17.5 bestätigt diese Erwartung: Nach Rückkehr der Methodeg ist dieses neue Array (mit den Werten lokaler undWert) verschwundenund s hat nach wie vor die vorherigen Werte.
17.3.8 Aufrufsemantik: Parameter (2)
Kompatibilitätsregeln
Wie bei allen Zuweisungen auch!
12 Liste von Definitionen u.ä.
Abbildung 17.3: Methodenaufruf mit Referenzparameter: Vor Zuweisung ing
Abbildung 17.4: Methodenaufruf mit Referenzparameter: Nach Zuweisung ing
Insgesamt
Im Endeffekt sehr ähnlich zu Python!
17.3.9 Aufrufsemantik: Rückgabewert
Rückgabetyp void
• Keine Rückgabe• Funktionsaufruf darf nicht an eine Variable zugewiesen werden
Rückgabetyp primitive type
• return derMethodemuss einAusdruck des entsprechenden (oder eineskompatiblen) Typs sein
17.3. Methoden 13
Abbildung 17.5: Nach Rückkehr aus g
• Wert wird aus Methode an die Variable des Aufrufers kopiert
Rückgabewert reference type
• return derMethodemuss einAusdruck des entsprechenden (oder eineskompatiblen) Typs sein
• Referenz auf Objekt wird kopiert
17.3.10 Aufrufsemantik: Rückgabewert (2)
Kompatibilitätsregeln
Wie bei allen Zuweisungen auch!
Insgesamt
Im Endeffekt sehr ähnlich zu Python!
17.3.11 Rückgabewert und kompatible Typen
Typ des return Ausdrucks muss zuweisungskompatibel zu Typ in Methoden-signatur sein
Beispiel 1: Funktioniert
1 class Ellipse {2 // (Daten weggelassen)3 double flaeche() {4 return 3.1415 * self.achse1 * self.achse2;5 }6 }
14 Liste von Definitionen u.ä.
17.3.12 Rückgabewert und kompatible Typen
Beispiel 2: Funktioniert auch – impliziter typecast
1 class Ellipse {2 // (Daten weggelassen)3 double flaeche() {4 return 3 * ((int) self.achse1) * ((int) self.achse2);5 }6 }
Beispiel 3: Scheitert – keine implizite Umwandlung möglich
1 class Ellipse {2 // (Daten weggelassen)3 int flaeche() {4 return 3.1415 * self.achse1 * self.achse2;5 }6 }
17.3.13 Mehrere Rückgabewerte
• Das kann Java nicht!• Lösung: Klassen definieren
– In Method Objekt erzeugen; mit Werten befüllen– Referenz auf diese Klasse zurückgeben
• Komplexer Ansatz– Führt zu vielen Pseudoklassen, nutzlose Namen, . . .
17.3.14 Lokale und globale Variablen
Lokale Variablen in Methoden
• Wie in Python: Methode darf am Anfang lokale Variable deklarieren• Verschwindet mit Ende der Methode
Globale Variablen
• Gibt es im eigentlichen Sinne nicht, da nichts ausserhalb einer Klasseexistiert
• Es gibt Klassenattribute, also auch Klassenvariablen– Wird mit Schlüsselwort static deklariert
17.3. Methoden 15
• Zugriff darauf benötigt kein global
17.3.15 Globale Variablen: Beispiel
1 class Ellipse {2 // Daten deklarieren:3 double x, y;4 double achse1, achse2;5 static int magic = 3;6
7 double flaeche() {8 return magic * this.achse1 * this.achse2;9 }10 }11
12 Ellipse e = new Ellipse();;13 e.achse1 = 2.3;14 e.achse2 = 4.5;15 System.out.println(e.flaeche());
e ==> Ellipse@59494225$4 ==> 2.3$5 ==> 4.531.049999999999997
17.3.16 Statische Methoden
Wenn es Klassenvariablen gibt, dann wohl auch Klassenmethoden?
• Ja, werden mit Schlüsselwort static deklariert• Kennen wir ja schon von public static void main!
16 Liste von Definitionen u.ä.
17.3.17 Statische und objektbezogene Attribute in Java
Objektbezogen StatischDeklaration ohne static mit staticExistenz separat in jedem Ob-
jekt1x pro Klasse
Attribut wird ange-legt
bei Objekterzeugung(new)
bei Laden der Klasse(meist: Programman-fang)
Attribut wird ver-nichtet
wenn Objekt vernich-tet wird (keine Refe-renz mehr existiert)
wenn Klasse entladen(meist: Programmen-de)
Konstruktur bei Objekterzeugung(new)
bei Laden der Klasse
Zugriff auf Datenat-tribut d
obj.d oder this.d Klassenname.d
Aufruf einer Me-thode d
obj.m() oderthis.m()
Llassenname.m()
17.3.18 Sichtbarkeit
Definition 17.2 (Sichtbarkeit einer Variablen in Java). Die Sichtbarkeit (oderder Gültigkeitsbereich) einer Variable erstreckt sich von der Deklaration derVariable bis zum Ende des Blocks, in dem die Deklaration stattfand.
Beispielsweise ist die Sichtbarkeit einer Objekt- oder Klassenvariable dieKlasse. Für sie gilt zudem eine Ausnahme: Sie sind auch schon vor ihrerDeklaration in allen Methoden der Klasse sichtbar.
Eine Variable kann durch eine gleichnamige Variable in einem enthaltenenBlock überdeckt werden. Eine Methode kann eine Variable deklarieren, dieeine Klassenvariable gleichen Namens überdeckt. (Ein normaler Block darfdas nicht.)
17.3.19 Lebensdauer
Definition 17.3 (Lebensdauer einer Variable in Java). Die Lebensdauer einerVariable ist die Zeitspanne, in der die Variable einen gültigen Wert besitzt(der Wert kann eine Referenz auf ein Objekt sein).
Lebensdauer vs. Sichtbarkeit
Nicht das gleiche!
• Lebendig, aber nicht sichtbar: Durch andere Variable überdeckt
17.3. Methoden 17
• Sichtbar, aber nicht lebendig: Deklariert, aber noch nicht mit Wert ver-sehen
17.3.20 Sichtbarkeit und Lebensdauer: Vergleich
• Die Konzepte sind in Java und Python grundsätzlich sehr ähnlich• Details unterscheiden sich natürlich
– Python definiert Lebensdauer von Variablen über Existenz des ent-sprechenden Namensraums
– Sonderfall von Klassenvariablen in Java– . . .
17.3.21 Konstruktoren
• Konstruktoren im wesentlichen wie in Python• Wir haben jeweils einen impliziten Konstruktor benutzt• Parameter für Konstruktoren wie bei Methoden
– Werte dafür: Parameter beim Klassennamen bei new• Konstruktoren können überladen werden
17.3.22 getter und setter
• getter und setter Methoden müssen von Anfang an da sein, wenn benö-tigt– Java hat keinen Mechanismus wie @Property in Python– Wesentlicher Grund: Funktionen sind nicht first-class citizens inJava
• Manche IDEs: Erzeugen automatisch für alle Attribute getter / setter– Unendliche lange, unübersichtliche Klassen
• (Die Existenz mächtiger IDEs ist ein Indiz für eine verkorkste Sprache)
17.3.23 Wrappers, Boxing
• Alle Klassen sind von Object abgeleitet– Damit alle Objekte mit Object zuweisungskompatibel
• Einfache Typen sind aber keine Klassen– int, char, . . .
• Wrapper-Klasse: einen normalen Wert in ein passendes Objekt ein-wickeln– Integer für int, Boolean für boolean, etc.
• Umständlich, deswegen weitgehend automatisch durch Compiler: Bo-xing und Unboxing
18 Liste von Definitionen u.ä.
1 Integer intObj = new Integer(42);2 int x = intObj.intValue();
intObj ==> 42x ==> 42
17.4 Einfachvererbung
17.4.1 Konzept
Klassenhierarchie die nur Einfachvererbung nutzen unterscheiden sich kaum
• Marginale Syntaxunterschiede• Unterschiede bzgl. Zugriffsrechten
17.4.2 Syntax
• class Unterklasse extends Oberklasse– Statt: class Unterklasse(Oberklasse):
• super in Java analog zu super bei Python– Solange nur Einfachvererbung!
• Beispiel
1 class Book extends Article {2 // ggf. neue Daten3 String ISBN;4 // ggf. neue Methoden oder Methoden überschreiben5 void showInfo() {6 // Aufruf der Methode der Oberklasse:7 super.showInfo();8 // und eigene Ausgabe9 System.out.println("ISBN: " + this.ISBN);10 }11 }
17.4.3 Kompatibilität bei Zuweisung
• Einer Variable vom Typ Oberklasse darf ein Objekt einer Unterklassezugewiesen werden
17.4. Einfachvererbung 19
• Umgekehrt: Nein!• Beispiel:
1 class Article {2 String artikelnummer;3 }4
5 class Book extends Article {6 String ISBN;7 }8
9 // das ist erlaubt:10 Book b1 = new Book();11 Article a1 = b1;12
13 // das scheitert, ein Artikel ist kein Buch:14 Article a2 = new Article();15 Book b2 = a2;
b1 ==> Book@5cb9f472a1 ==> Book@5cb9f472a2 ==> Article@56ef9176
17.4.4 Zugriff auf Felder nach Zuweisung zu Variable eines Oberklassen-typs
• Kann man nach Zuweisung zu einer Oberklassentypisierten Variablenoch auf die Daten (oder Methoden) der Unterklasse zugreifen?– Die sind in dem Objekt ja noch vorhanden– Es wurde ja nur der Typ der Referenz geändert; am Objekt selbst istnichts passiert!
1 class Article {2 String artikelnummer;3 }4
5 class Book extends Article {6 String ISBN;7 }8
9 // das ist erlaubt:
20 Liste von Definitionen u.ä.
10 Book b1 = new Book();11 b1.ISBN = "abc";12 System.out.println(b1.ISBN);13
14 // Zuweisung an Oberklasse15 Article a1 = b1;16 // funktioniert das?17 System.out.println(a1.ISBN);
b1 ==> Book@56ef9176$4 ==> "abc"abca1 ==> Book@56ef9176
Fehlermeldung
Nein!
• Der Compiler prüft schon, ob man über eine Article-Referenz auf einAttribut ISBN zugreifen kann
• Das gibt es in der Article-Klasse aber nicht, also Fehlermeldung– Der Compiler weiß nicht, dass es sich in Wirklichkeit um ein Bookhandelt
– Das könnte man erst zur Laufzeit feststellen
17.4.5 Unterklasse zurückbekommen? Typecasts zwischen Objekten?
• Aber man könnte das doch typecasten?• Wir zwingen den Compiler, eine Interpretation der Referenz als Bookzu akzeptieren!
• (Vergleich Python: Keine Notwendigkeit für Typecasts zwischen Klassen)
1 class Article {2 String artikelnummer;3 }4
5 class Book extends Article {6 String ISBN;7 }8
9 // das ist erlaubt:10 Book b1 = new Book();11 b1.ISBN = "abc";
17.4. Einfachvererbung 21
12
13 Article a1 = b1;14 Book b2 = (Book) a1;15 System.out.println(b2.ISBN);
b1 ==> Book@56ef9176$4 ==> "abc"a1 ==> Book@56ef9176b2 ==> Book@56ef9176abc
17.4.6 Typecast wenn falsches Objekt?
• Was passiert, wenn wir einen typecast (irrtümlich?) auf ein falschesObjekt anwenden?
• Beispiel:
1 class Article {2 String artikelnummer;3 }4
5 class Book extends Article {6 String ISBN;7 }8
9 Article a1 = new Article();10
11 // das scheitert; a1 ist kein Book12 Book b2 = (Book) a1;13 System.out.println(b2.ISBN);
a1 ==> Article@56ef9176
Fehlermeldung
Auch das scheitert!
• Der Typecast ist nur die Anweisung einer anderen Interpretation• Dadurch entsteht kein Book, keine ISBN aus dem Nichts!• Zur Laufzeit wird aber sehr wohl das Vorhandensein von ISBN überprüft,nicht gefunden
• Führt zu Exception (ClassCastException)
22 Liste von Definitionen u.ä.
17.4.7 Vor einem Typecast nachschauen? Reflektion?
• Kann man bei einem Objekt nachschauen, ob es zu einer bestimmtenKlasse gehört?
• Schlüsselwort: instanceof– Pendant zu isinstance-Funktion in Python
1 class Article {2 String artikelnummer;3 }4
5 class Book extends Article {6 String ISBN;7 }8
9 // das scheitert; b2 ist kein Book10 Article a1 = new Article();11 if (a1 instanceof Book) {12 Book b2 = (Book) a1;13 System.out.println(b2.ISBN);14 } else {15 System.out.println("Leider kein Buch!");16 }
a1 ==> Article@5cb9f472Leider kein Buch!
17.4.8 Dynamische Methodenbindung
Welche Methoden werden aufgerufen in einer Klassenhierarchie?
• Genauer: Liegt die Wahl der Methode am:– Typ des referenzierten Objektes oder– Typ der referenzierenden Variable?
• Erwartungshaltung? Sinnvoll?
17.4.9 Dynamische Methodenbindung: Beispiel
1 class Article {2 void m() { System.out.println("m von Artikel"); }3 }4
5 class Book extends Article {
17.4. Einfachvererbung 23
6 void m() { System.out.println("m von Buch"); }7 }8
9 Article a = new Article();10 Book b = new Book();11 // das hier ist ja klar:12 a.m();13 b.m();14
15 // was passiert hier?16 Article a1 = b;17 a1.m();18
19 Book b1 = (Book) a1;20 b1.m();
a ==> Article@56ef9176b ==> Book@1ed4004bm von Artikelm von Bucha1 ==> Book@1ed4004bm von Buchb1 ==> Book@1ed4004bm von Buch
17.4.10 Dynamische Methodenbindung: Beobachtung
Wie zu erwarten (und erhofft): Der Typ des Objekts entscheidet!
• Java nutzt (wie Python und alle OO-Sprachen) dynamische Bindung zurMethodenauswahl– Dynamisch: Zur Laufzeit, nicht zum Zeitpunkt der Compilierung
• Der Typecastb1 = (Book) a1 ist an dieser Stelle also nicht notwendig,um die richtigeMethode aufzurufen
Bemerkung: Andere Sprachen
• Dynamische Bindung ist ein unverzichtbaresMerkmal vonOO-Sprachen– Es gibt Sprachen (wie C++), bei denen man das durch ein Schlüssel-wort bei der Methoden angeben muss – aber es geht!
• Hätte man keine dynamische Bindung, würde man auf lange Abfragenmit instanceof verfallen
• Und selbst das funktioniert nur für schon bekannte Klassen
24 Liste von Definitionen u.ä.
– Wird eine neue Klasse abgeleitet, würde trotzdem die falsche Me-thode (die der Oberklasse) aufgerufen
• Dynamische Bindung erlaubt das Aufrufen einer Methode, die zum Zeit-punkt der Implementierung des Aufrufs noch gar nicht existierte!
17.4.11 Sichtbarkeit zwischen Klassen
• Sichtbarkeit oben: Von Deklaration bis Ende des Blocks• Also eigentlich: In Oberklasse deklarierte Methode nicht in Unterklassesichtbar?– Naja . . . doch– Aber . . . je nachdem
Sicherbarkeit bei Vererbung
• Java verfolgt ein Misstrauen-basiertes Sichtbarkeitsmodell zwischenOber- und Unterklassen
• Attribute (Daten und Methoden) können mit Zugriffsregeln annotiertwerden
17.4.12 Sichtbarkeit zwischen Klassen: Annotationen
• In Klasse selbst: Alle Attribute sichtbar (gemäß obiger Regeln)• Im Verhältnis zu anderen Klassen: Annotationen
– public: Attribute darf von (Methoden von) allen anderen Klassenbenutzt werden (lesen oder schreiben)
– protected: Nur Objekte einer Unterklasse dürfen Attribut benut-zen
– private: Nur Methoden der Klasse selbst dürfen Attribute benut-zen
17.4.13 Sichtbarkeit zwischen Klassen: Zugriff von außen
Zugriff von außen/aus anderen Klassen:
1 class A {2 private int u;3 protected int v;4 public int w;5 int x;6
7 void m() {8 this.u = 5;9 this.v = 5;
17.4. Einfachvererbung 25
10 this.w = 5;11 this.x = 5;12 }13 }14
15 // Zugriff von aussen:16 A a = new A();17 a.u = 42;18 a.v = 42;19 a.w = 42;20 a.x = 42;21
22 // Zugriff auf Attribute aus eigener Methode:23 a.m();
a ==> A@cb644e$3 ==> 42$4 ==> 42$5 ==> 42
Fehlermeldungen
• Zugriff a.u und a.v scheitert
17.4.14 Sichtbarkeit zwischen Klassen: Zugriff von Unterklasse
1 class A {2 private int u;3 protected int v;4 public int w;5 int x;6 }7
8 class B extends A {9 void m() {10 // Compiler detektiert das Problem,11 // verweigert Übersetzung12 this.u = 5;13 this.v = 5;14 this.w = 5;15 this.x = 5;16 }17 }
26 Liste von Definitionen u.ä.
18
19 B a = new B();20 b.m();
Fehlermeldung
• Klasse B kann nicht erzeugt werden mit Zugriff auf this.u
17.4.15 Sichtbarkeit zwischen Klassen: Zugriff von Unterklasse und su-per
Das funktioniert: Methode A.m darf auf privates Attribut A.u zugreifen
1 class A {2 private int u;3 protected int v;4 public int w;5 int x;6
7 void m() {8 this.u = 5;9 }10 }11
12 class B extends A {13 void m() {14 super.m();15 this.v = 5;16 this.w = 5;17 this.x = 5;18 }19 }20
21 B b = new B();22 b.m();
17.4.16 Sichtbarkeit zwischen Klassen: ÜbersichtAnnotation Klasse selbst Unterklasse Andere Klasse/von aussenpublic Ja Ja Japrotected Ja Ja Neinkeine Angabe Ja Ja Neinprivate Ja Nein Nein
17.4. Einfachvererbung 27
Anmerkung: Wozu protected?
Der Unterschied zwischenprotected und keiner Angabe kommt imnächstenKapitel.
17.4.17 Sichtbarkeit zwischen Klassen: Misstrauen
• Oracle Empfehlung: Misstraue Deinen Kindern– Zitat:
* Use the most restrictive level that makes sense. Use privateunless you have a good reason not to.
* Avoid public fields.• Daraus folgendes Muster
– Datenattribute in der Regel private, ggf. protected– Mit getter und setterMethoden
• Aus Sprach-Praxis folgt Notwendigkeit für IDEs!
17.4.18 Vertrauen zwischen Klassen: final
final für Methoden
• Man kann das Überschreiben von Methoden verbieten• Annotation: Schlüsselwort final• Nutzen der Konstruktion hochgradig umstritten
– Siehe Gründe für final – lesen Sie das kritisch durch!– (Man braucht das, um Closures nachzubauen, die Java eigentlichnicht hat. . . )
final für Daten
• Mit finalmarkiertes Datenattribut: eine Konstante oder eine Variable,die in der Methode nicht mehr verändert wird
• Das kann natürlich sinnvoll sein
1 class Math {2 public final double pi = 3.14159296;3 }
17.4.19 Warum Misstrauen?
• Avoid public: Weil Java keinen Property-Mechanismus hat und nichtnachträglich getter und setter-Methoden einfügen kann
• Weil Java-Entwickler den Programmierern von Unterklassen nichts zu-traut
28 Liste von Definitionen u.ä.
– Vielleicht: aus Erfahrung geboren?– It is for your own protection– Idee: Information Hiding (Geheimnisprinzip)
* Verstecke Realisierung von Daten; erlaube Zugriff nur überMethoden
• Alternative: Python we are all adults• Stellen Sie sich den Skandal vor!!
17.5 Mehrfachvererbung und Ersatz dafür
17.5.1 Mehrfachvererbung in Java
• Gibt es nicht• Java-Entwickler: Damit sind Programmierer überfordert
Abbildung 17.6: Entwickler sind mit Mehrfachvererbung überfordert
17.5.2 Gefahr Mehrfachvererbung in Java
• Tatsächlich: Durch die deklarierten Datenattribute in Java wäre eineMehrfachvererbung komplizierter zu definieren als in Python!
• Also: Diamond of Death for Data!• Problem: Zwei Oberklassen definieren beide ein Attribut int x
– Oder: eine Oberklasse int x, eine andere double x?– Welches x ist dann in der Unterklasse gemeint? Gibt es eins oderzwei?
Übliche Antwort
• Es gibt zwei x in Unterklasse, Variablenname . . . (z.B. C++)• Variablenname mit Name der Oberklasse als Präfix
17.5.3 Ersatz: Abstrakte Klassen und Interfaces
• Aber man braucht natürlich schon die Möglichkeit, einer Klasse unter-schiedliche Eigenschaften, von mehreren Eltern, zu geben
17.5. Mehrfachvererbung und Ersatz dafür 29
• Java macht dazu eine relative komplizierte Ersatzkonstruktion: Interfa-ces– Die angeblich ganz einfach ist
• Allerdings müssen wir dazu etwas Anlauf nehmen: abstrakte Klassen
17.5.4 Abstrakte Klassen: Tierfarm als Beispiel
• Nehmen wir an, wir wollen eine Tierfarm modellieren– (Vergleiche Kapitel 13, Mössenböck)
• UML-Diagramm könnte wie folgt aussehen
Abbildung 17.7: UML-Diagramm einer einfachen Tierfarm
17.5.5 Implementierung von Tier?
• Was soll man bei Tier als Methoden implementieren?– Kein sinnvoller Inhalt
• Die Methode weglassen ist aber auch keine Option– Die Kasse soll ja die Gemeinsamkeiten aller Tiere beschreiben
17.5.6 Lösung: Methode als Platzhalter markieren
• Schlüsselwort abstract vor Methodentyp– Beispiel: public abstract void, private abstract int
• Markiert die Methode als Platzhalter; keine Implementierung nötig– Kein {} nach dem Funktionskopf, stattdessen ;
• Klassemitmindestens einer abstraktenMethode ist eine abstrakte Klasse– Explizit machen: auch die Klasse muss abstractmarkiert sein!
• Beispiel:
1 abstract class Tier {2 boolean frisst (String food) { return false; }3 abstract void sprich ();4 }
30 Liste von Definitionen u.ä.
17.5.7 Objekte von abstrakten Klassen?
• Tier hat also eine Methode, die es geben muss• Die es aber nicht gibt!• Tier ist also unvollständig• Konsequenz: Von abstrakten Klassen kann kein Objekt instantiiert werden!
– Was sollte man auch tun, wenn bei so einem Objekt die Methodesprich aufgerufen würde?
17.5.8 Unterklassen von abstrakten Klassen: FehlendeMethoden ergän-zen
• Abstrakte Klasse in Unterklasse ableiten• Dort die fehlende Methode(n) implementieren• Wenn alle abstrakten Methoden konkret geworden sind, kann man auchObjekte instantiieren
• Beispiel:
1 abstract class Tier {2 boolean frisst (String food) { return false; }3 abstract void sprich ();4 }5
6 class Hund extends Tier {7 // Hunde sind Allesfresser:8 boolean frisst (String food) { return true; }9 void sprich () { System.out.println("woof!"); }10 }11
12 Hund h = new Hund();13 h.sprich();
h ==> Hund@56ef9176woof!
17.5.9 Variablen abstrakter Klassen speichernReferenz auf konkreteUn-terklasse
• Abstrakte Klasse instantiieren nicht möglich• Aber sehr wohl möglich: Referenz auf konkretes Objekt speichern• Und dort konkrete Methoden aufrufen• Wenig überraschend . . .
17.5. Mehrfachvererbung und Ersatz dafür 31
1 abstract class Tier {2 boolean frisst (String food) { return false; }3 abstract void sprich ();4 }5
6 class Hund extends Tier {7 // Hunde sind Allesfresser:8 boolean frisst (String food) { return true; }9 void sprich () { System.out.println("woof!"); }10 }11
12 Hund h = new Hund();13 Tier t = h;14 t.sprich();
h ==> Hund@56ef9176t ==> Hund@56ef9176woof!
17.5.10 Variablen abstrakter Klassen speichernReferenz auf konkreteUn-terklasse (2)
• Typisch: Abstrakte Klasse in Methodensignatur nutzen– Konkrete Referenz bei Aufruf zuweisen– Methoden kann dadurch mit allen Unterklassen arbeiten
• Beispiel: Tierfarm tierunabängig realisiert
17.5.11 Variablen abstrakter Klassen speichernReferenz auf konkreteUn-terklasse (3)
1 abstract class Tier {2 boolean frisst (String food) { return false; }3 abstract void sprich ();4 }5
6 class Hund extends Tier {7 // Hunde sind Allesfresser:8 boolean frisst (String food) { return true; }9 void sprich () { System.out.println("woof!"); }10 }11
12 class Maus extends Tier {
32 Liste von Definitionen u.ä.
13 // Mäuse fressen nur Käse:14 boolean frisst (String food) { return (food == "Käse"); }15 void sprich () { System.out.println("meep"); }16 }17
18 class Tierfarm {19 Tier [] meineTiere;20 Tierfarm() { this.meineTiere = new Tier[0]; }21 Tierfarm(Tier[] anfangstiere) { this.meineTiere = anfangstiere; }22
23 void konzert() { this.konzert(this.meineTiere); }24 void konzert(Tier [] andereTiere) { for (Tier t: andereTiere) t.sprich(); }25 }26
27 Tierfarm farm = new Tierfarm(new Tier[] { new Hund(), new Maus()});28 farm.konzert();29 Tierfarm farm2 = new Tierfarm();30 farm2.konzert( new Tier[] { new Maus(), new Hund ()});
ch(); }farm ==> Tierfarm@25bbe1b6woof!meepfarm2 ==> Tierfarm@4b952a2dmeepwoof!
17.5.12 Alle Methoden abstrakt
• Natürlich können alle Methoden einer abstrakten Klasse abstrakt sein• Nennen wir so eine Klasse vollständig abstrakt
– Achtung, keine Standard-Terminologie!
17.5.13 Datenattribute weglassen
• Man kann eine vollständig abstrakte Klasse ohne Datenattribute dekla-rieren– Unüblich, aber warum nicht . . .
• Nennen wir das eine leere vollständig abstrakte Klasse (kur: LeVAK)– Achtung, keine Standard-Terminologie!
17.5. Mehrfachvererbung und Ersatz dafür 33
17.5.14 Mehrverfachererbung mit LeVAKs?
• Könnte man es riskieren (Programmierern zumuten), mit LeVAKs Mehr-fachvererbung zu erlauben?
• Semantik klar?– Diamond of death für Datenattribute tritt nicht mehr auf– Diamond of death für Methoden schon noch. . .
* Aber wäre das ein Problem?* Die Methoden sind ja leer, ohne Implementierung!* Es tritt also kein Problemmehrdeutiger Implementierungen auf!
17.5.15 Mehrverfachererbung mit LeVAKs – Beispiel
Also könnte man folgendes schreiben?
• (Achtung, kein gültiges Java!)
1 abstract class A {2 abstract a1 ();3 abstract a2 ();4 abstract a3 ();5 ...6 abstract m();7 }8
9 abstract class B {10 abstract b1 ();11 abstract b2 ();12 ...13 abstract m();14 }15
16 class C extends A, B { /* hier Implementierung der Methoden */ }
17.5.16 Nutzen Mehrfachvererbung nur mit LeVAKs?
• Aber das ist nur bedingt nützlich: Keine einzige Implementierung vor-handen
• Vererbung eigentlich: Methode für Code Reuse!!• Klasse Cmüsste alle Methoden selbst implementieren
– Wenn Name mehrfach auftritt (m) ist nicht schlimm: wird ohnehinneu implementiert!
34 Liste von Definitionen u.ä.
Schnittstelle!
• Lediglich: Schnittstelle der Unterklasse spezifiziert• Nutzer eines C-Objektes kann sich auf die Existenz
– der Methoden a1, a2, a3, . . . und b1, b2, . . .– sowie einer eindeutig definiertenMethode m verlassen!
17.5.17 Mehrverfachererbung: LeVAKs, und eine richtige Klasse?
• Vielleicht: eine nicht-abstrakte Oberklasse, sonst nur LeVAKs?– Sonst ja gar keine Funktionalität vorhanden. . .
• Semantik klar?– Kein Diamond of Death for Data– Diamond of Death for methods?
* Gleicher Methodenname zwischen LeVAKs: nicht schlimm, wieoben
* GleicherMethodenname zwischen LeVAKsundnicht-abstrakterOberklasse: nicht schlimm; die Implementierung ist ja eindeu-tig!
17.5.18 Mehrverfachererbung: LeVAKs, und eine richtige Klasse – Risiko?Nutzen?
• Das ist lange nicht so nützlich wie echteMehrfachvererbung• Aber möglicherweise traut das selbst Java den Programmierern zu?
17.5.19 Javas Einstellung
• Nein, nein, nein – das ist immer noch zu kompliziert für Programmierer!• Wir müssen Programmierer wenigstens warnen, wenn sie sowas machenwollen
• Wir führen lieber neues Schlüsselwort ein!
17.5.20 Interfaces statt LeVAKs, neue Schlüsselworte
• Ein LeVAK bekommt einen Namen: interface– Das ist plausibel– Mehr als eine Beschreibung einer (noch zu realisierenden) Schnitt-stelle ist das ja auch nicht
• Aber von einem interface kann man nicht erben (nicht extends)– Sondern nur implementieren: implement
17.5.21 interface und extend: Beispiel
17.5. Mehrfachvererbung und Ersatz dafür 35
1 interface foo {2 void foo();3 }4
5 interface bar {6 void bar();7 }8
9 class A {10 public void foo() { System.out.println("foo"); }11 }12
13 class B extends A implements foo, bar {14 public void bar() { System.out.println("BAR"); }15 }16
17 B b = new B();18 b.foo();19 b.bar();
b ==> B@25bbe1b6fooBAR
17.5.22 interface und extend
• interface kann mit extend vererbt werden• Aber es ist keine abstrakte Klasse, nein, nein!
17.5.23 interface vs. LeVAK
• Eine leere, vollständig abstrakte Klasse– Keine Daten– Alle Methoden abstract
• Ein interface– Keine Daten– Alle Methoden abstract
• Semantik bei inheritance exakt gleich!– Man muss interface und implements benutzen– Siehe Interface considered harmful
36 Liste von Definitionen u.ä.
Abbildung 17.8: Unterschied zwischen abstract class und interface
17.5.24 interface: Die kleinen Details
public
• Alle Methoden eines Interfaces sind public• Implementierende Methoden der Klasse sind zwangsläufig ebenfallspublic
Konstanten
• Interfaces dürfen Konstanten festlegen (Daten mit final)• Aber damit: Deadly diamond of death für Konstanten!• Lösung wie bei C++: Klassennamen voranstellen! :-)
17.5.25 interface in Java 8
• Java 8 fügt einenneuenAspekt zuinterfacehinzu:default-ImplementierungJava docs– Java 8: Derzeit aktuelle Sprachversion (März 2014, mit laufendenUpdates)
– interface darf eine Standard-Implementierung für eineMethodevorsehen
– Wird benutzt, wenn implementierende Klasse diese Methode nichtüberschreibt
• Das ist natürlich keineMehrfachvererbung – nein, nein
Abbildung 17.9: defaults in interfaces
17.5. Mehrfachvererbung und Ersatz dafür 37
17.5.26 interface in Java 8
• Beispiel siehe unten• Argumentation in Java 8 für default: interface soll sich entwickelndürfen– Ohne Änderung der implementierenden Klassen zu erfordern
1 public interface vehicle {2 default void print(){3 System.out.println("I am a vehicle!");4 }5 }
17.5.27 interface in Java 8: Diamond of Death!
• Implementierende Klasse muss mehrdeutige Methoden überschreiben• Zugriff auf mehrdeutige Methode: Der Name des Interfaces muss beisuper vorangestellt werden
• Anders formuliert: Java 8 hat keine klar definierte Method Resolu-tion Order!
• Genauer gesagt:– Implementierungen in Klassen Vorrang über default in Interface– Spezifischeres Interface hat Vorrang– Aber damit ist der Diamond of Death nicht aufgelöst!– Compiler liefert Fehler
Grund?
Grund für seltsame Struktur: Wunsch nach Rückwärtskompatibilität
17.5.28 interface in Java 8: Diamond of Death!
Fehlermeldung bei Übersetzung:class D inherits unrelated defaultsfor hello() from types C and B
1 interface A { default void hello() { System.out.println("from A"); } }2
3 interface B extends A { default void hello() { System.out.println("from B"); } }4 interface C extends A { default void hello() { System.out.println("from C"); } }5
6 class D implements C, B { }
38 Liste von Definitionen u.ä.
17.5.29 Einfach- oder Mehrfachvererbung?
• Java: Mehrfachvererbung ist zu kompliziert; wir müssen das vermeiden– Ziel: Diamond of Death vermeiden– Aber reine Einfachvererbung funktioniert in Praxis nicht– Führen wir zusätzliche Sprachkonstrukte ein– Über mehrere Sprachversionen: Konzepte der Mehrfachvererbungeingeschlichen
– Damit insgesamt: Komplizierte Mischform• Python: Mehrfachvererbung kann man durchaus erklären
– Insgesamt bleibt die Sprache sauberer– Erfodert aber ggf. mehr Nachdenken beim Programmieren
17.6 Zusammenfassung, Einordnung
17.6.1 Zusammenfassung
• Javas Objektorientierung ist bei einfachen Klassenhierarchien rechtähnlich zu Pythons Ansatz
• Wesentlicher Unterschied:– Verzicht auf Mehrfachvererbung– Imitation durch Interfaces– Misstrauensmodell und feingranulare Zugriffskontrolle auf Daten,Methoden, Klassen* Bis hin zu private und final – Nutzen stark strittig
17.6.2 Nichts ist umsonst!
• Java diente als Beispiel einer statisch typisierten Sprache– Java ist nicht die einzige mögliche Sprache aus dieser Familie
• Die unbestrittenen Vorteile erkauft man sich durch– erhöhte Komplexität der Syntax (Typdeklarationsteilsprache)– komplizierteres Modell der Variablenhandhabung
• Und diesen Abtausch gibt es imwesentlichen in allen statisch typisiertenSprachen!– Das ist kein Versagen von Java
17.6.3 Sprachen sind auch nur ein Werkzeug!
• Alle Sprachen haben ihre Vor- und Nachteile
17.6. Zusammenfassung, Einordnung 39
– Und: Persönliche Präferenzen, Stilfragen, Ästhetik, Commnunity,. . .
• Letztlich ist das nicht der entscheidende Punkt!– Mit jeder Sprache kann man alles machen
• Wichtig: Vertrautheit, Routine, Werkzeuge, Bibliotheken• Unwichtig: Die neueste Mode
17.6.4 Grenzen von Sprachen
Meines Erachtens mit großer Vorsicht zu geniessen:
[Was kann man denken?]Language shapes the way we think and determines what we
can think about. (B. L. Whorf)Die Grenzen meiner Sprache bedeuten die Grenzen meiner
Welt. (L. Wittgenstein)