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 8 Schnittstellen, Aufzählungen, versiegelte Klassen, Records
Pfeil 8.1 Schnittstellen
Pfeil 8.1.1 Schnittstellen sind neue Typen
Pfeil 8.1.2 Schnittstellen deklarieren
Pfeil 8.1.3 Abstrakte Methoden in Schnittstellen
Pfeil 8.1.4 Implementieren von Schnittstellen
Pfeil 8.1.5 Ein Polymorphie-Beispiel mit Schnittstellen
Pfeil 8.1.6 Die Mehrfachvererbung bei Schnittstellen
Pfeil 8.1.7 Keine Kollisionsgefahr bei Mehrfachvererbung *
Pfeil 8.1.8 Erweitern von Interfaces – Subinterfaces
Pfeil 8.1.9 Konstantendeklarationen bei Schnittstellen
Pfeil 8.1.10 Nachträgliches Implementieren von Schnittstellen *
Pfeil 8.1.11 Statische ausprogrammierte Methoden in Schnittstellen
Pfeil 8.1.12 Erweitern und Ändern von Schnittstellen
Pfeil 8.1.13 Default-Methoden
Pfeil 8.1.14 Erweiterte Schnittstellen deklarieren und nutzen
Pfeil 8.1.15 Öffentliche und private Schnittstellenmethoden
Pfeil 8.1.16 Erweiterte Schnittstellen, Mehrfachvererbung und Mehrdeutigkeiten *
Pfeil 8.1.17 Bausteine bilden mit Default-Methoden *
Pfeil 8.1.18 Markierungsschnittstellen *
Pfeil 8.1.19 (Abstrakte) Klassen und Schnittstellen im Vergleich
Pfeil 8.2 Aufzählungstypen
Pfeil 8.2.1 Methoden auf Enum-Objekten
Pfeil 8.2.2 Aufzählungen mit eigenen Methoden und Initialisierern *
Pfeil 8.2.3 enum mit eigenen Konstruktoren *
Pfeil 8.3 Versiegelte Klassen und Schnittstellen
Pfeil 8.3.1 Versiegelte Klassen und Schnittstellen (sealed classes/interfaces)
Pfeil 8.3.2 Unterklassen sind final, sealed, non-sealed
Pfeil 8.3.3 Abkürzende Schreibweisen
Pfeil 8.4 Records
Pfeil 8.4.1 Einfache Records
Pfeil 8.4.2 Records mit Methoden
Pfeil 8.4.3 Konstruktoren von Records anpassen
Pfeil 8.4.4 Konstruktoren ergänzen
Pfeil 8.4.5 Versiegelte Schnittstellen und Records
Pfeil 8.4.6 Zusammenfassung
Pfeil 8.5 Zum Weiterlesen
 

Zum Seitenanfang

8.3    Versiegelte Klassen und Schnittstellen Zur vorigen ÜberschriftZur nächsten Überschrift

Bevor wir zum eigentlichen Thema, versiegelte Klassen, kommen, wollen wir kurz die Eigenschaften der bisher besprochenen Typen wiederholen:

Typ

Instanzanzahl

Unterklassen möglich

anpassbares Verhalten

veränderbare Zustände

reguläre Klassendeklaration

beliebig

ja

möglich

ja

Klasse mit abstrakter Methode

beliebig

Muss

möglich

ja

Klasse mit finaler Methode

beliebig

ja

Ja, nur nicht die finale Methode

ja

finale Klasse

beliebig

nein

nein

ja

Aufzählungstyp

fest

nein

ja

nein

Tabelle 8.3     Möglichkeiten und Einschränkungen der verschiedener Typen in Java

Die Tabelle macht deutlich, dass die einzelnen Typen sich immer irgendwie etwas unterscheiden – bei einer Art ist etwas verboten, bei einer anderen Art wiederum explizit notwendig.

Bei Aufzählungen lassen sich selbst keine Instanzen bilden, sondern die Objekte werden intern gebildet. Es gibt so viele Instanzen, wie es Elemente in dem Aufzählungstyp gibt. Explizite Unterklassen sind von Aufzählungstypen prinzipiell nicht möglich, indirekt allerdings schon. Dazu wird im ersten Schritt eine abstrakte Methode im Aufzählungstyp eingeführt (oder eine Schnittstelle implementiert), die dann die einzelnen Aufzählungselemente implementieren können. So gesehen lassen sich von nichtfinalen regulären Klassen beliebig viele Unterklassen bilden, aber von Aufzählungstypen nur beschränkt viele, und das auch nur intern; die Unterklassen selbst sind gar nicht sichtbar, sondern werden versteckt. Aufzählungstypen dürfen auch keine veränderbaren Zustände speichern, sie müssen Konstanten bleiben.

Nehmen wir an, wir möchten ausdrücken, dass das Ergebnis einer Operation gültig oder ungültig war. Das lässt sich über einen Aufzählungstyp ausdrücken, etwa so:

enum Result {

Failure, Success

}

Eine Methode könnte etwas vom Typ Result liefern:

public class Baking {

static Result cake() { Failure oder Success zurückgeben }

}

Die Aufrufer wissen, dass sie nur mit zwei Arten von Result rechnen müssen: Failure oder Success – etwas anderes ist unmöglich, es kommen keine weiteren Result-Typen dazu.

Jetzt könnte aber die Anforderung kommen, dass ein Zustand mit gespeichert werden soll, dass etwa beide eine beliebige Nachricht merken können oder der Fehler einen Fehlercode haben soll. Schon sind Aufzählungstypen raus, und Klassen kommen ins Spiel. Alternativ:

abstract class Result {

Object body;

}

class Success extends Result { }

class Failure extends Result {

final int errorCode;

}

Mit dieser Implementierung kann der Zustand gespeichert werden. Kommen wir noch einmal zur Methode cake():

Result cake() { ... }

Die Methode liefert wieder Result, doch der Empfänger hat ein Problem, denn neben Success oder Failure könnte es von der Klasse Result noch beliebig viele Unterklassen geben, die cake() aufbauen und liefern könnte.

Gesucht ist eine Möglichkeit, die Anzahl möglicher Unterklassen zu beschränken, aber prinzipiell beliebig viele Instanzen zuzulassen, die unterschiedliche Zustände haben können. Und dann sind wir bei den versiegelten Klassen (engl. sealed classes).

 

Zum Seitenanfang

8.3.1    Versiegelte Klassen und Schnittstellen (sealed classes/interfaces) Zur vorigen ÜberschriftZur nächsten Überschrift

Bei versiegelten Klassen ist im Vorfeld bekannt, welche Klassen sie erweitern. Der Compiler kann damit gewisse Prüfungen vornehmen, etwa auf die Vollständigkeit bei switch, wie beim enum.

Versiegelte Klassen sind nichtfinal und führen mit permits die erlaubten Unterklassen auf:

Listing 8.32     src/main/java/com/tutego/insel/sealed/Result.java, Result

abstract sealed class Result permits Failure, Success {

final Object body;

public Result( Object body ) { this.body = body; }

}

Ob die Oberklasse abstrakt ist oder nicht, spielt für permits keine Rolle, in unserem Fall muss es nicht direkt Result-Instanzen geben. permits ist ein wenig wie ein aufgeweichtes final; bei final darf es gar keine Unterklassen geben, bei permits immerhin ein paar spezielle.

In unserem Beispiel darf es nur die zwei Unterklassen Failure und Success geben. Die Unterklassen sind in unserem Beispiel final, damit nicht nachträglich noch neue Typen auftauchen; wir werden gleich sehen, dass es Alternativen gibt.

Listing 8.33     src/main/java/com/tutego/insel/sealed/Success.java, Success

final class Success extends Result {

Success( Object body ) { super( body ); }

}

Listing 8.34     src/main/java/com/tutego/insel/sealed/Failure.java, Failure

final class Failure extends Result {

final int errorCode;

Failure( int errorCode, Object body ) {

super( body );

this.errorCode = errorCode;

}

}

Eine Methode wie cake() kann nur Exemplare von Success und Failure aufbauen:

Listing 8.35     src/main/java/com/tutego/insel/sealed/Baking.java, Baking

public class Baking {

static Result cake() {

return Math.random() > 0.5 ? new Success( "Yummy" )

: new Failure( 29, "Burned" );

}

}

Der Empfänger weiß, wie bei den Aufzählungstypen zuvor, dass nur Success oder Failure möglich ist:

Listing 8.36     src/main/java/com/tutego/insel/sealed/OperationDemo.java, main()

var result = Baking.cake();

if ( result instanceof Success )

System.out.println( "Success: " + result.body );

else

System.out.println( "Failure: " + result.body );

Bei der Fallunterscheidung gibt es nur zwei Fälle zu unterscheiden. Wir hätten ein großes Problem, wenn plötzlich eine dritte Result-Unterklasse einzieht, denn die würde in dem bestehenden Code wie ein Failure behandelt.

[»]  Ausblick

Bei Sealed Classes ist im Vorfeld bekannt, wie viele Unterklassen es genau gibt. JEP 406: Pattern Matching for switch[ 178 ](https://openjdk.java.net/jeps/406) bereitet den Weg für ein neues Feature, das es nicht final in Java 17 geschafft hat. Die Idee daher ist, Code wie den folgenden

if ( result instanceof Success success ) ...

else if ( result instanceof Failure failure ) ...

else throw ...

umzuschreiben in:

switch ( result ) {

case Success success -> ...

case Failure failure -> ...

}

Der Compiler weiß, dass es nur zwei konkrete Typen gibt, und daher ist kein Default-Zweig nötig.

Neben Klassen können auch Schnittstellen ihre erlaubten Implementierungen aufzählen; wir werden in Abschnitt 8.4, »Records«, noch ein Beispiel sehen.

 

Zum Seitenanfang

8.3.2    Unterklassen sind final, sealed, non-sealed Zur vorigen ÜberschriftZur nächsten Überschrift

Eine Unterklasse einer sealed Oberklasse muss entweder final, sealed oder non-sealed sein. Ein Beispiel zu final haben wir gesehen – finale Klassen verbieten weitere Unterklassen. Allerdings kann eine Unterklasse selbst wieder sealed sein und damit explizit neue Unterklassen aufführen.

Der Modifizierer non-sealed ist ein wenig exotisch, weil er das einzige (kontextuelle) Schlüsselwort ist, das ein Minuszeichen enthält. Die Bedeutung ist, dass die Versiegelung aufgehoben wird und es beliebig viele Unterklassen geben kann.

In der Mathematik gibt es kommutative Operationen, wo die Argumente einer Operation vertauscht werden können, ohne dass sich das Ergebnis verändert. Addition und Multiplikation sind kommutativ, Subtraktion und Division nicht. Das können wir abbilden:

Listing 8.37     src/main/java/com/tutego/insel/sealed/BinaryOperation.java, Ausschnitt

abstract sealed class BinaryOperation

permits Commutative, Noncommutative { }

abstract sealed class Commutative extends BinaryOperation

permits Addition, Multiplication { }

final class Addition extends Commutative { }

final class Multiplication extends Commutative { }

non-sealed class Noncommutative extends BinaryOperation { }

class Subtraction extends Noncommutative { }

Bei den kommutativen Operationen sind nur Exemplare von Addition und Multiplication erlaubt, es lassen sich keine Unterklassen bilden. Von Noncommutative lassen sich jedoch beliebige Unterklassen bilden.

 

Zum Seitenanfang

8.3.3    Abkürzende Schreibweisen Zur vorigen ÜberschriftZur nächsten Überschrift

Es gibt zwei Sonderfälle, bei denen permits entfällt.

Unterklassen in gleicher Compilationseinheit

Das Schlüsselwort permits kann wegfallen, wenn die Unterklassen sich in der gleichen Compilationseinheit befinden:

Listing 8.38     src/main/java/com/tutego/insel/sealed/State.java, Ausschnitt

public sealed class State { }

final class Open extends State { }

final class Closed extends State { }

Geschachtelte Typen

In Java lassen sich Typdeklarationen in andere Typdeklarationen setzen und damit eine enge Bindung der Typen ausdrücken. Das wird noch einmal Thema in Kapitel 10, »Geschachtelte Typen«, werden, hier schon einmal ein Vorgriff:

Listing 8.39     src/main/java/com/tutego/insel/sealed/Feeling.java, Feeling

public sealed class Feeling {

public enum Scale {

Not_at_all, A_little, Moderately, Quite_a_lot, Extremely

}



public final Scale scale;



protected Feeling( Scale scale ) { this.scale = Objects.requireNonNull( scale ); }



public static final class Friendly extends Feeling {

public Friendly( Scale scale ) { super( scale ); }

}

public static final class Tense extends Feeling {

public Tense( Scale scale ) { super( scale ); }

}

public static final class Active extends Feeling {

public Active( Scale scale ) { super( scale ); }

}

}

Ein Beispiel für die Nutzung der Typen:

Feeling active = new Feeling.Active( Feeling.Scale.Moderately );

System.out.println( active.scale );

 


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