Kapitel 17: Objektorientierung in...

39
Kapitel 17: Objektorientierung in Java Grundlagen der Programmierung 1 Holger Karl Wintersemester 2016/2017 Inhaltsverzeichnis Inhaltsverzeichnis 1 Abbildungsverzeichnis 2 Liste von Definitionen u.ä. 2 17.1 Überblick ............................... 3 17.2 Klassen ................................ 3 17.3 Methoden .............................. 7 17.4 Einfachvererbung .......................... 18 17.5 Mehrfachvererbung und Ersatz dafür ............... 28 17.6 Zusammenfassung, Einordnung .................. 38 1

Transcript of Kapitel 17: Objektorientierung in...

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)