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.3    Typen in Hierarchien Zur vorigen ÜberschriftZur nächsten Überschrift

Die Vererbung bringt einiges Neues in Bezug auf Kompatibilität von Typen mit. Dieser Abschnitt beschäftigt sich mit den Fragen, welche Typen kompatibel sind und wie sich ein Typ zur Laufzeit testen lässt.

 

Zum Seitenanfang

7.3.1    Automatische und explizite Typumwandlung Zur vorigen ÜberschriftZur nächsten Überschrift

Fassen wir noch einmal zusammen, was wir bisher für Typen haben: Die Klassen Nap und Workout haben wir als Unterklassen von Event modelliert. Die eigene Oberklasse Event erweitert selbst keine explizite Oberklasse, sodass implizit java.lang.Object die Oberklasse ist. In Event gibt es die Objektvariablen about und duration, die Nap und Workout erben, und Workout hat zusätzlich caloriesBurned für die »verbrannten« Kalorien.

Ist-eine-Art-von-Beziehung und die automatische Typumwandlung

Mit der Ist-eine-Art-von-Beziehung ist eine interessante Eigenschaft verbunden, die wir bemerken, wenn wir die Zusammenhänge zwischen den Typen beachten:

  • Ein Event ist ein Event.

  • Ein Nap ist ein Nap.

  • Ein Workout ist ein Workout.

  • Ein Nap ist ein Event.

  • Ein Workout ist ein Event.

  • Ein Event ist ein java.lang.Object.

  • Ein Nap ist ein java.lang.Object.

  • Ein Workout ist ein java.lang.Object.

Kodieren wir das in Java:

Listing 7.19     src/main/java/com/tutego/insel/game/c/vl/TypeSuptype.java, Ausschnitt

Nap    napAsNap    = new Nap();

Event napAsEvent = new Nap();

Object napAsObject = new Nap();



Workout workoutAsWorkout = new Workout();

Event workoutAsEvent = new Workout();

Object workoutAsObject = new Workout();

Es gilt also, dass immer dann, wenn ein Typ gefordert ist, auch ein Untertyp erlaubt ist. Der Compiler führt eine implizite Typumwandlung durch. Wir werden uns dieses sogenannte liskovsche Substitutionsprinzip im folgenden Abschnitt anschauen.

Was wissen Compiler und Laufzeitumgebung über unser Programm?

Compiler und Laufzeitumgebung haben verschiedene Sichten auf das Programm und wissen unterschiedliche Dinge. Durch den Einsatz von new gibt es zur Laufzeit nur zwei Arten von Objekten: Nap und Workout. Auch dann, wenn es

Event workoutAsEvent = new Workout();

heißt, referenziert workoutAsEvent zur Laufzeit ein Workout-Objekt. Der Compiler aber »vergisst« dies und glaubt, workoutAsEvent wäre nur ein einfaches Event.

Der Compiler bekommt nur einen Ausschnitt zu sehen und zieht seine eigenen Schlüsse

Abbildung 7.10     Der Compiler bekommt nur einen Ausschnitt zu sehen und zieht seine eigenen Schlüsse

In der Klasse Event sind jedoch nur about und duration deklariert, aber keine Objektvariable caloriesBurned, obwohl das tatsächliche Workout-Objekt natürlich ein caloriesBurned hat. Auf caloriesBurned können wir aber erst einmal nicht zugreifen:

System.out.println( workoutAsEvent.about );

System.out.println( workoutAsEvent.caloriesBurned );

// Cannot resolve symbol 'caloriesBurned'

Schreiben wir noch einschränkender

System.out.println( workoutAsObject.about );

// Cannot resolve symbol 'about'

so steht hinter der Referenzvariablen workoutAsObject immer noch ein vollständiges Workout-Objekt, aber weder caloriesBurned noch about/duration sind nutzbar; es bleiben nur die Fähigkeiten aus java.lang.Object.

[»]  Begrifflichkeit

Es gibt in Java zwei Typsysteme: die vom Compiler und die der Laufzeitumgebung. Um den Typ, den der Compiler kennt, von dem Typ, den die JVM kennt, zu unterscheiden, nutzen wir die Begriffe Referenztyp und Objekttyp. Im Fall von Event e = new Workout() ist Event der Referenztyp und Workout der Objekttyp (Merkhilfe: Es steht ein Objekt zur Laufzeit im Speicher). Der Compiler sieht nur den Referenztyp, aber nicht den Objekttyp. Vereinfacht gesagt: Der Compiler interessiert sich bei einer Konstruktion wie Event e = new Workout() nur für den linken Teil Event e und die Laufzeitumgebung nur für den rechten Teil e = new Workout().

Explizite Typumwandlung

Diese Typeinschränkung gilt auch an anderer Stelle. Ist eine Variable vom Typ Room deklariert, können wir die Variable nicht mit einem »kleineren« Typ initialisieren:

Event    event   = new Workout();    // Workout ist Objekttyp zur Laufzeit

Workout running = event; // inline image

// Incompatible types. Found: '[...].Event', required: '[...].Workout'

Auch wenn zur Laufzeit event ein Workout referenziert, können wir running nicht damit initialisieren. Der Compiler kennt event nur unter dem »kleineren« Typ Event, und das reicht nicht zur Initialisierung des »größeren« Typs Workout.

Es ist aber möglich, das Objekt hinter event durch eine explizite Typumwandlung für den Compiler wieder zu einem vollwertigen Workout mit verbrauchten Kalorien zu machen:

Listing 7.20     src/main/java/com/tutego/insel/game/c/vl/TypeSuptype.java, Ausschnitt

Event    event   = new Workout();

Workout running = (Workout) event;

System.out.println( running.caloriesBurned );

Unmögliche Anpassung und ClassCastException

Dies funktioniert aber lediglich dann, wenn event auch wirklich ein Workout-Objekt referenziert. Der Compiler lässt sich zu einer ungültigen Typumwandlung überreden, und auch Folgendes wird ohne Fehler in Bytecode übersetzt:

Listing 7.21     src/main/java/com/tutego/insel/game/c/vl/TypeSuptype.java, Ausschnitt

Event    event   = new Nap();

Workout running = (Workout) event; // inline image ClassCastException

System.out.println( running.caloriesBurned );

Der Compiler macht diesen Unsinn mit, aber nicht die JVM; zur Laufzeit kommt es bei diesem Kuckucksobjekt zu einer ClassCastException:

Exception in thread "main" java.lang.ClassCastException: class [...].Nap cannot be cast to class [...].Workout ([...].Nap and [...].Workout are in unnamed module of loader 'app')

at ...
[»]  Hinweis

Wenn wir var nutzen, dann wird der Typ der Variablen automatisch so sein wie der auf der rechten Seite. Beispiel: Schreiben wir

Event event = new Nap()

haben wir uns gezielt bei event für den Typ Nap entschieden. Wir sollten uns bewusst sein, dass bei einer Variablendeklaration mit var die neue Variable exakt den Typ der rechten Seite bekommt.

var event = new Nap();

Hier ist event ein Nap, hat und kann also mehr als ein Event. Es kann bei der Programmierung relevant sein, den Typ zu beschränken.

 

Zum Seitenanfang

7.3.2    Das Substitutionsprinzip Zur vorigen ÜberschriftZur nächsten Überschrift

Nehmen wir an, wir möchten herausfinden, wie lange insgesamt zwei Ereignisse dauern, wobei wir bei Ereignissen von Nap und Workout sprechen. Die erste Idee wäre vermutlich, drei Methoden zu deklarieren, die mit allen Kombinationen von Nap und Workout aufgerufen werden können:

int totalDuration( Nap event1, Nap event2 ) {

return event1.duration + event2.duration;

}

int totalDuration( Workout event1, Nap event2 ) {

return event1.duration + event2.duration;

}

int totalDuration( Workout event1, Workout event2 ) {

return event1.duration + event2.duration;

}

Uns dürfte sofort auffallen, dass der Rumpf der Methoden identisch ist. Und genau hier wird uns die Vererbung helfen, den Code zu verallgemeinern. Zur Verdeutlichung zwei Szenarien aus dem echten Leben:

  • Packen wir einen Koffer, dann kommen dort unterschiedliche Dinge hinein. Wir haben nicht einen speziellen Koffer für die Unterwäsche, dann einen zweiten Koffer für die Hemden und einen dritten Koffer für die Kopfhörer, die wir immer verlieren …

  • Stellen wir uns vor, Bekannte kommen ausgehungert von einer Wandertour zurück und fragen: »Haste was zu essen?« Die Frage zielt wohl darauf ab, dass es bei Hunger ziemlich egal ist, was wir anbieten, wichtig ist nur etwas Essbares. Daher können wir Eis, aber auch gegrillte Heuschrecken und Hákarl (isländischer fermentierter Hai) anbieten, denn das ist alles essbar.

Diese Ausgangslage führt uns zu einem wichtigen Konzept in der Objektorientierung: »Wer wenig will, kann viel bekommen.« Genauer gesagt: Wenn Unterklassen wie Nap oder Workout die Oberklasse Event erweitern, können wir überall, wo etwas vom Typ Event gefordert wird, auch ein Nap- oder Workout-Objekt übergeben, da beide ja vom Typ Event sind und wir mit der Unterklasse nur spezieller werden. Auch können wir weitere Unterklassen von Event und Nap übergeben, da auch Unterklassen das »Gen« Event in sich tragen. Alle diese Dinge wären vom Typ Event und daher typkompatibel.

Im nächsten Beispiel gibt es eine Methode totalDuration(Event… events), die eine variable Argumentliste von Event-Objekten annimmt und die Summe aller Dauern berechnet. Der Methode ist der Objekttyp ganz egal, denn wenn die Methode ein Event erwartet, könnten wir ein Objekt exakt vom Objekttyp Event, aber auch Nap oder Workout übergeben. Jedes Event hat grundsätzlich die Objektvariablen about und duration, da ja alle Unterklassen die Eigenschaften erben und Unterklassen die Eigenschaften nicht »wegzaubern« können.

Listing 7.22     src/main/java/com/tutego/insel/game/c/vl/TotalDuration.java, TotalDuration

public class TotalDuration {

public static int totalDuration( Event... events ) {

int sum = 0;

for ( Event event : events )

sum += event.duration;

return sum;

}



public static void main( String[] args ) {

Workout running = new Workout();

running.duration = 50;

Event sleeping = new Nap();

sleeping.duration = 40;

System.out.println( totalDuration( running, sleeping ) );

}

}

Mit Event haben wir eine Basisklasse geschaffen, die allen Unterklassen Grundfunktionalität beibringt, in unserem Fall die Objektvariablen about und duration. So liefert die Basisklasse einen gemeinsamen Nenner, etwa gemeinsame Objektvariablen oder Methoden, die jede Unterklasse besitzen wird. Das ist viel flexibler, als für die konkreten Typen Nap und Workout Spezialmethoden zu schreiben. Denn wenn in einer Anwendung später neue Event-Typen auftauchen, behandelt totalDuration(Event…) diese ganz selbstverständlich mit, und wir müssen den »Algorithmus« nicht anfassen.

Weil anstelle eines Objekts auch ein Objekt der Unterklasse auftauchen kann, sprechen wir von Substitution. Das Prinzip wurde von der Professorin Barbara Liskov[ 163 ](Die Zeitschrift »Discover« zählt sie zu den 50 wichtigsten Frauen in der Wissenschaft. ) formuliert und heißt daher auch liskovsches Substitutionsprinzip.

In der Java-Bibliothek finden sich zahllose weitere Beispiele. Die println(Object)-Methode ist so ein Beispiel. Die Methode nimmt beliebige Objekte entgegen, denn der Parametertyp ist Object. Die Substitution besagt, dass wir alle Objekte dort einsetzen können, da alle Klassen von Object abgeleitet sind.

 

Zum Seitenanfang

7.3.3    Typen mit dem instanceof-Operator testen Zur vorigen ÜberschriftZur nächsten Überschrift

Im vorangegangenen Beispiel haben wir gesehen, dass der Methode totalDuration(…) eine variable Anzahl von Events-Objekten übergeben werden kann, und damit kann die Methode auf alles zurückgreifen, was ein Event hat. Wenn wir »mehr« übergeben, also konkrete Unterklassen, dann weiß die Methode das nicht. Hin und wieder ist es nötig, den konkreten Typ zu kennen, und der relationale Operator instanceof hilft dabei, Exemplare auf ihre Verwandtschaft mit einem Referenztyp zu prüfen. Er stellt zur Laufzeit fest, ob eine Referenz ungleich null und von einem bestimmten Typ ist. Der Operator ist binär, hat also zwei Operanden.

Ein Beispiel, das in Summation von Zeiten nichts addiert, was vom Typ Nap ist:

Listing 7.23     src/main/java/com/tutego/insel/game/c/vl/TotalDuration.java, totalDurationOfNoNapEvents

public static int totalDurationOfNoNapEvents( Event... events ) {

int sum = 0;

for ( Event event : events )

if ( event instanceof Nap )

sum += event.duration;

return sum;

}
[»]  Hinweis

Der Operator instanceof testet ein Objekt auf seine Hierarchie. So ist zum Beispiel o instanceof Object für jedes Objekt o wahr, denn jedes Objekt ist immer Kind von java.lang.Object. Die Programmiersprache Smalltalk unterscheidet hier mit zwei Nachrichten isMemberOf (exakt) und isKindOf (wie Javas instanceof). Um den exakten Typ zu testen, lässt sich mit dem Class-Objekt arbeiten, etwa wie im Ausdruck o.getClass() == Object.class, der testet, ob o genau ein Object-Objekt ist.

Der Compiler kann prinzipiell einige Typen schon kennen, doch der Test wird wirklich zur Laufzeit gemacht. Formulieren wir ein Beispiel, um zu sehen, dass instanceof wirklich zur Laufzeit den Test durchführen muss. In allen Fällen ist das Objekt zur Laufzeit ein Workout:

Listing 7.24     src/main/java/com/tutego/insel/game/c/vl/InstanceofDemo.java, main

Workout wo1 = new Workout();

System.out.println( wo1 instanceof Workout ); // true

System.out.println( wo1 instanceof Event ); // true

System.out.println( wo1 instanceof Object ); // true



Event wo2 = new Workout();

System.out.println( wo2 instanceof Workout ); // true

System.out.println( wo2 instanceof Event ); // true

System.out.println( wo2 instanceof Object ); // true

System.out.println( wo2 instanceof Nap ); // false



Object wo3 = new Workout();

System.out.println( wo3 instanceof Workout ); // true

System.out.println( wo3 instanceof Event ); // true

System.out.println( wo3 instanceof Object ); // true

System.out.println( wo3 instanceof Nap ); // false

System.out.println( wo3 instanceof String ); // false

Keine beliebigen Typtests mit instanceof

Der Compiler lässt aber nicht alles durch. Liegen zwei Typen überhaupt nicht in der Typhierarchie, lehnt der Compiler den Test ab, da die Vererbungsbeziehungen schon inkompatibel sind:

System.out.println( "Toll" instanceof StringBuilder );

// inline image Incompatible conditional operand types String and StringBuilder

Der Ausdruck ist falsch, da StringBuilder keine Basisklasse für String ist.

Zum Schluss:

Object ref1 = new int[ 100 ];

System.out.println( ref1 instanceof String );

System.out.println( new int[100] instanceof String ); // inline image Compilerfehler
[»]  Hinweis

Mit instanceof lässt sich der Programmfluss anhand der tatsächlichen Typen steuern, etwa mit Anweisungen wie if(reference instanceof Typ) A else B. In der Regel zeigt Kontrolllogik dieser Art aber tendenziell ein Designproblem an und kann oft anders gelöst werden. Das dynamische Binden ist so eine Lösung; sie wird später in Abschnitt 7.5, »Drum prüfe, wer sich dynamisch bindet«, vorgestellt.

instanceof und null

Ein instanceof-Test mit einer Referenz, die null ist, gibt immer false zurück:

String ref2 = null;

System.out.println( ref2 instanceof String ); // false

System.out.println( ref2 instanceof Object ); // false

Das leuchtet ein, denn null entspricht ja keinem konkreten Objekt.

[+]  Tipp

Da instanceof einen null-Test enthält, sollte statt etwa

if ( s != null && s instanceof String )

immer vereinfacht so geschrieben werden:

if ( s instanceof String )
 

Zum Seitenanfang

7.3.4    Pattern-Matching bei instanceof Zur vorigen ÜberschriftZur nächsten Überschrift

Der Java-Compiler und die Laufzeitumgebung besitzen unterschiedlich leistungsstarke Typsysteme. Während die Laufzeitumgebung immer den präzisen Objekttyp kennt, so ist dem Compiler nur der Referenztyp bekannt, und das kann ein Basistyp vom Objekttyp sein. Wenn z. B. eine Referenzvariable vom Typ Object ist und zur Laufzeit ein String referenziert wird, dann ist der Objekttyp String, und der Compiler kennt den Typ nicht, sondern weiß nur, dass es irgendwie ein Object ist.

Mitunter muss im Code eine Weiche programmiert werden, die den tatsächlichen Objekttyp berücksichtigt. Zur Laufzeit lässt sich der Objekttyp mit dem instanceof-Operator testen. Es ist nicht ungewöhnlich, dass es in Programmen Fallunterscheidungen gibt, die zunächst mit instanceof oder mit getClass() den genauen Typ testen und dann eine Referenzvariable auf einen Untertyp anpassen.

Schauen wir uns ein Beispiel an. Eine Methode soll zwei Event-Parameter bekommen, und wenn diese Ereignisse vom Typ Workout sind, soll die Anzahl verbrauchter Kalorien extrahiert und verglichen werden. Eine Variante könnte wie folgt aussehen:

Listing 7.25     src/main/java/com/tutego/insel/game/c/vl/PatternMatchingInstanceOf.java, Ausschnitt

static boolean burnedSameCalories( Event event1, Event event2 ) {

if ( !(event1 instanceof Workout && event2 instanceof Workout) )

return false;

Workout workout1 = (Workout) event1;

Workout workout2 = (Workout) event2;

return workout1.caloriesBurned == workout2.caloriesBurned;

}

Der Lösungsansatz arbeitet nach dem Fail-fast-Verfahren. Früh wird getestet, ob die beiden Events überhaupt vom Typ Workout sind. Wenn nicht, wird die Methode mit false verlassen. Sind dann beide Event-Objekte vom Typ Workout, findet eine explizite Typumwandlung statt; die Kalorien werden extrahiert und verglichen.

Es gibt eine andere Möglichkeit, das zu programmieren, und zwar, in der Fallunterscheidung nicht die Methode zu verlassen, sondern im Fall von speziellen Workout-Objekten im Rumpf der Fallunterscheidung den Vergleich vorzunehmen:

static boolean burnedSameCalories( Event event1, Event event2 ) {

if ( event1 instanceof Workout && event2 instanceof Workout ) {

Workout workout1 = (Workout) event1;

Workout workout2 = (Workout) event2;

return workout1.caloriesBurned == workout2.caloriesBurned;

}

return false;

}

In beiden Fällen lässt sich im Code gut ablesen, dass zunächst ein instanceof-Test steht und anschließend eine neue Variable deklariert wird, die durch eine Typumwandlung aus einem anderen Ausdruck, hier einer Variablen, hervorgeht.

Solche Fälle sind gar nicht so selten. Deswegen wurde in Java 16 der instanceof-Operator im Rahmen des »JEP 394: Pattern Matching for instanceof«[ 164 ](https://openjdk.java.net/jeps/394) erweitert. Und zwar kann hinter dem zu testenden Typ ein Bezeichner für eine neue Variable stehen, die genau dann initialisiert wird, wenn der instanceof-Test wahr ist. In Java wird das Sprachfeature Pattern matching bei instanceof genannt. Das Pattern ist genau dieser instanceof-Test, und die Variable nennt sich Pattern-Variable. Die Pattern-Variable wird nur dann gesetzt, wenn der Test erfolgreich war. Andernfalls wird die Variable nicht initialisiert, und es lässt sich nicht darauf zurückgreifen.

Mit zwei Pattern-Variablen können wir unser Programm ein wenig kürzer formulieren:

static boolean burnedSameCalories( Event event1, Event event2 ) {

if ( event1 instanceof Workout workout1 && event2 instanceof Workout workout2 )

return workout1.caloriesBurned == workout2.caloriesBurned;

return false;

}

Das Beispiel zeigt, dass im Rumpf der Fallunterscheidung auf die initialisierten Variablen workout1 und workout2 zurückgegriffen werden kann.

Wir können aber auch genau den anderen Ansatz wählen, dass bei unpassenden Typen die Methode verlassen wird:

static boolean burnedSameCalories( Event event1, Event event2 ) {

if ( !(event1 instanceof Workout workout1 && event2 instanceof Workout workout2) )

return false;

return workout1.caloriesBurned == workout2.caloriesBurned;

}

Bei diesem Lösungsansatz fällt gut auf, dass die Variablen für den verbleibenden Codeblock deklariert bleiben.

Der Compiler kann ganz präzise verfolgen, an welcher Stelle die Pattern-Variable gültig und initialisiert ist, was eine weitere Variante zulässt:

static boolean burnedSameCalories( Event event1, Event event2 ) {

return event1 instanceof Workout workout1 && event2 instanceof Workout workout2

&& workout1.caloriesBurned == workout2.caloriesBurned;

}

Hier findet das sogenannte flow scoping statt. Die Auswertung der Operanden beim &&-Operator geschieht von links nach rechts. Da der hier eingesetzte Und-Operator nach dem Kurzschlussverfahren arbeitet, werden so viele Teile ausgewertet, bis die Antwort feststeht, also früher abgebrochen, wenn ein Operand false ist. Da jedoch am Anfang schon die Pattern-Variablen eingeführt wurden, weiß der Compiler nach zwei gültigen Typprüfungen, das beim dritten Ausdruck – dem Vergleich – zwei Variablen workout1 und workout2 vom Typ Workout existieren.

 


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