Rheinwerk Computing < openbook >


 
Inhaltsverzeichnis
Materialien zum Buch
Vorwort
1 Java ist auch eine Sprache
2 Imperative Sprachkonzepte
3 Klassen und Objekte
4 Arrays und ihre Anwendungen
5 Der Umgang mit Zeichen und Zeichenketten
6 Eigene Klassen schreiben
7 Objektorientierte Beziehungsfragen
8 Schnittstellen, Aufzählungen, versiegelte Klassen, Records
9 Ausnahmen müssen sein
10 Geschachtelte Typen
11 Besondere Typen der Java SE
12 Generics<T>
13 Lambda-Ausdrücke und funktionale Programmierung
14 Architektur, Design und angewandte Objektorientierung
15 Java Platform Module System
16 Die Klassenbibliothek
17 Einführung in die nebenläufige Programmierung
18 Einführung in Datenstrukturen und Algorithmen
19 Einführung in grafische Oberflächen
20 Einführung in Dateien und Datenströme
21 Einführung ins Datenbankmanagement mit JDBC
22 Bits und Bytes, Mathematisches und Geld
23 Testen mit JUnit
24 Die Werkzeuge des JDK
A Java SE-Module und Paketübersicht
Stichwortverzeichnis


Buch bestellen
Ihre Meinung?



Spacer
<< zurück
Java ist auch eine Insel von Christian Ullenboom

Einführung, Ausbildung, Praxis
Buch: Java ist auch eine Insel


Java ist auch eine Insel

Pfeil 7 Objektorientierte Beziehungsfragen
Pfeil 7.1 Assoziationen zwischen Objekten
Pfeil 7.1.1 Unidirektionale 1:1-Beziehung
Pfeil 7.1.2 Zwei Freunde müsst ihr werden – bidirektionale 1:1-Beziehungen
Pfeil 7.1.3 Unidirektionale 1:n-Beziehung
Pfeil 7.2 Vererbung
Pfeil 7.2.1 Vererbung in Java
Pfeil 7.2.2 Ereignisse modellieren
Pfeil 7.2.3 Die implizite Basisklasse java.lang.Object
Pfeil 7.2.4 Einfach- und Mehrfachvererbung *
Pfeil 7.2.5 Sehen Kinder alles? Die Sichtbarkeit protected
Pfeil 7.2.6 Konstruktoren in der Vererbung und super(…)
Pfeil 7.3 Typen in Hierarchien
Pfeil 7.3.1 Automatische und explizite Typumwandlung
Pfeil 7.3.2 Das Substitutionsprinzip
Pfeil 7.3.3 Typen mit dem instanceof-Operator testen
Pfeil 7.3.4 Pattern-Matching bei instanceof
Pfeil 7.4 Methoden überschreiben
Pfeil 7.4.1 Methoden in Unterklassen mit neuem Verhalten ausstatten
Pfeil 7.4.2 Mit super an die Eltern
Pfeil 7.5 Drum prüfe, wer sich dynamisch bindet
Pfeil 7.5.1 Gebunden an toString()
Pfeil 7.5.2 Implementierung von System.out.println(Object)
Pfeil 7.6 Finale Klassen und finale Methoden
Pfeil 7.6.1 Finale Klassen
Pfeil 7.6.2 Nicht überschreibbare (finale) Methoden
Pfeil 7.7 Abstrakte Klassen und abstrakte Methoden
Pfeil 7.7.1 Abstrakte Klassen
Pfeil 7.7.2 Abstrakte Methoden
Pfeil 7.8 Weiteres zum Überschreiben und dynamischen Binden
Pfeil 7.8.1 Nicht dynamisch gebunden bei privaten, statischen und finalen Methoden
Pfeil 7.8.2 Kovariante Rückgabetypen
Pfeil 7.8.3 Array-Typen und Kovarianz *
Pfeil 7.8.4 Dynamisch gebunden auch bei Konstruktoraufrufen *
Pfeil 7.8.5 Keine dynamische Bindung bei überdeckten Objektvariablen *
Pfeil 7.9 Zum Weiterlesen und Programmieraufgabe
 

Zum Seitenanfang

7.5    Drum prüfe, wer sich dynamisch bindet Zur vorigen ÜberschriftZur nächsten Überschrift

Bei der Vererbung haben wir eine Form der Ist-eine-Art-von-Beziehung, sodass die Unterklassen immer auch vom Typ der Oberklassen sind. Die sichtbaren Methoden, die die Oberklassen besitzen, existieren somit auch in den Unterklassen. Der Vorteil bei der Spezialisierung ist, dass die Oberklasse eine einfache Implementierung vorgibt und eine Unterklasse diese überschreiben kann. Wir hatten das bisher bei toString() gesehen. Doch nicht nur die Spezialisierung ist aus Sicht des Designs interessant, sondern auch die Bedeutung der Vererbung. Bietet eine Oberklasse eine sichtbare Methode an, so wissen wir immer, dass alle Unterklassen diese Methode haben werden, egal, ob sie die Methode überschreiben oder nicht. Wir werden gleich sehen, dass dies zu einem der wichtigsten Konstrukte in objektorientierten Programmiersprachen führt.

 

Zum Seitenanfang

7.5.1    Gebunden an toString() Zur vorigen ÜberschriftZur nächsten Überschrift

Da jede Klasse Eigenschaften von java.lang.Object erbt, lässt sich auf jedem Objekt die toString()-Methode aufrufen. In unseren Beispielen haben die Klassen Object, Event, Workout alle eine eigene toString()-Methode. Event überschreibt toString() von Object, Workout überschreibt toString() von Event.

Es gibt nun ein interessantes Szenario, wenn die toString()-Methode aufgerufen wird, aber der Referenztyp und Objekttyp unterschiedlich sind.

Listing 7.35     src/main/java/com/tutego/insel/game/c/vn/DynamicBinding.java, main

Workout ww = new Workout();

ww.about = "Running";

ww.duration = 100;

ww.caloriesBurned = 300;

System.out.println( ww.toString() );



Event ew = new Workout();

ew.about = "Running";

ew.duration = 100;

System.out.println( ew.toString() );



Object ow = new Workout();

System.out.println( ow.toString() );

Dreimal findet ein Aufruf von toString() statt, wobei der Objekttyp immer gleich bleibt (Workout), aber der Referenztyp immer ein anderer ist (Workout, Event, Object).

Jetzt ist die spannendste Frage in der gesamten Objektorientierung folgende: Was passiert bei dem Methodenaufruf toString()? Antwort:

Workout[about=Running, duration=100][caloriesBurned=300]

Workout[about=Running, duration=100][caloriesBurned=0]

Workout[about=null, duration=0][caloriesBurned=0]

Die Ausgabe ist leicht zu verstehen, wenn wir berücksichtigen, dass es zwei Typsysteme gibt und der Compiler nicht die gleiche Weisheit besitzt wie die Laufzeitumgebung. Entscheidend ist, dass die Laufzeitumgebung beim Methodenaufruf auf den Objekttyp schaut und nicht auf den Referenztyp – das ist das gleiche Verhalten wie bei instanceof. Da dem im Programmtext vereinbarten Variablentyp nicht zu entnehmen ist, welche Implementierung der Methode toString() aufgerufen wird, sprechen wir von später dynamischer Bindung, kurz dynamischer Bindung. Erst zur Laufzeit (das ist spät, im Gegensatz zur Übersetzungszeit) wählt die Laufzeitumgebung dynamisch die entsprechende Objektmethode aus – passend zum tatsächlichen Typ des aufrufenden Objekts. Die virtuelle Maschine weiß, dass hinter den drei Variablen jeweils ein Workout-Objekt steht, und ruft daher das toString() vom Workout auf.

Programmiersprachenvergleich

Dynamische Bindung ist in Java automatisch und lässt sich auch nicht über einen Modifizierer steuern. In C++ muss das Schlüsselwort virtual vor der virtuellen Funktion stehen, damit dynamisch gebunden wird.

Wichtig ist, dass eine Methode überschrieben wird. Nehmen wir an, es gäbe in Object kein toString(), sondern nur eine Implementierung in den Unterklassen Nap und Workout. Davon hätten wir nichts! Wir nutzen daher ausdrücklich die Gemeinsamkeit, dass Event, Workout und weitere Unterklassen toString() aus Object erben. Ohne die Oberklasse gäbe es kein Bindeglied, und folglich bietet die Oberklasse immer eine Methode an, die Unterklassen überschreiben können. Würden wir eine neue Unterklasse von Object schaffen und toString() nicht überschreiben, so fände die Laufzeitumgebung toString() in Object, aber die Methode gäbe es auf jeden Fall – entweder die Originalmethode oder die überschriebene Variante.

[»]  Begrifflichkeit

Dynamische Bindung wird oft auch Polymorphie genannt; ein dynamisch gebundener Aufruf ist dann ein polymorpher Aufruf. Das ist im Kontext von Java in Ordnung, allerdings gibt es in der Welt der Programmiersprachen unterschiedliche Dinge, die »Polymorphie« genannt werden, etwa parametrische Polymorphie (in Java heißt das Generics), und die Theoretiker kennen noch viel mehr beängstigende Begriffe.

 

Zum Seitenanfang

7.5.2    Implementierung von System.out.println(Object) Zur vorigen ÜberschriftZur nächsten Überschrift

Werfen wir einen Blick auf ein Programm, das dynamisches Binden noch deutlicher macht. Die print*(…)-Methoden sind so überladen, dass sie jedes beliebige Objekt annehmen und dann die String-Repräsentation ausgeben:

Listing 7.36     java/io/PrintStream.java, Skizze von println()

public void println( Object x ) {

String s = String.valueOf( x );

// String s = (obj == null) ? "null" : obj.toString();

synchronized ( this ) {

print( s );

newLine();

}

}

Die println(Object)-Methode besteht aus drei Teilen: Als Erstes wird die String-Repräsentation eines Objekts erfragt – hier findet sich der dynamisch gebundene Aufruf –, dann wird dieser String an print(String) weitergegeben, und newLine() produziert abschließend den Zeilenumbruch.

Der Compiler hat überhaupt keine Ahnung, was x ist; es kann alles sein, denn alles ist ein java.lang.Object. Statisch lässt sich aus dem Argument x nichts ablesen, und so muss die Laufzeitumgebung entscheiden, an welche Klasse der Methodenaufruf geht. Das ist das Wunder der dynamischen Bindung.

inline image  IntelliJ zeigt bei der Tastenkombination (Strg)+(H) eine Typhierarchie an, standardmäßig die Oberklassen und bekannten Unterklassen.

 


Ihre Meinung?

Wie hat Ihnen das Openbook gefallen? Wir freuen uns immer über Ihre Rückmeldung. Schreiben Sie uns gerne Ihr Feedback als E-Mail an kommunikation@rheinwerk-verlag.de

<< zurück
 Zum Rheinwerk-Shop
Zum Rheinwerk-Shop: Java ist auch eine Insel Java ist auch eine Insel

Jetzt Buch bestellen


 Buchempfehlungen
Zum Rheinwerk-Shop: Captain CiaoCiao erobert Java

Captain CiaoCiao erobert Java




Zum Rheinwerk-Shop: Algorithmen in Java

Algorithmen in Java




Zum Rheinwerk-Shop: Spring Boot 3 und Spring Framework 6

Spring Boot 3 und Spring Framework 6




Zum Rheinwerk-Shop: Java SE 9 Standard-Bibliothek

Java SE 9 Standard-Bibliothek




 Lieferung
Versandkostenfrei bestellen in Deutschland, Österreich und in die Schweiz

InfoInfo



 

 


Copyright © Rheinwerk Verlag GmbH 2024

Für Ihren privaten Gebrauch dürfen Sie die Online-Version natürlich ausdrucken. Ansonsten unterliegt das Openbook denselben Bestimmungen, wie die gebundene Ausgabe: Das Werk einschließlich aller seiner Teile ist urheberrechtlich geschützt.

Alle Rechte vorbehalten einschließlich der Vervielfältigung, Übersetzung, Mikroverfilmung sowie Einspeicherung und Verarbeitung in elektronischen Systemen.

 

[Rheinwerk Computing]



Rheinwerk Verlag GmbH, Rheinwerkallee 4, 53227 Bonn, Tel.: 0228.42150.0, Fax 0228.42150.77, service@rheinwerk-verlag.de



Cookie-Einstellungen ändern