Rheinwerk Computing < openbook >


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


Download:

- Listings, ca. 2,7 MB


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 9 Geschachtelte Typen
Pfeil 9.1 Geschachtelte Klassen, Schnittstellen und Aufzählungen
Pfeil 9.2 Statische geschachtelte Typen
Pfeil 9.3 Nichtstatische geschachtelte Typen
Pfeil 9.3.1 Exemplare innerer Klassen erzeugen
Pfeil 9.3.2 Die this-Referenz
Pfeil 9.3.3 Vom Compiler generierte Klassendateien *
Pfeil 9.3.4 Erlaubte Modifizierer bei äußeren und inneren Klassen
Pfeil 9.4 Lokale Klassen
Pfeil 9.4.1 Beispiel mit eigener Klassendeklaration
Pfeil 9.4.2 Lokale Klasse für einen Timer nutzen
Pfeil 9.5 Anonyme innere Klassen
Pfeil 9.5.1 Nutzung einer anonymen inneren Klasse für den Timer
Pfeil 9.5.2 Umsetzung innerer anonymer Klassen *
Pfeil 9.5.3 Konstruktoren innerer anonymer Klassen
Pfeil 9.6 Zugriff auf lokale Variablen aus lokalen und anonymen Klassen *
Pfeil 9.7 this in Unterklassen *
Pfeil 9.7.1 Geschachtelte Klassen greifen auf private Eigenschaften zu
Pfeil 9.8 Nester
Pfeil 9.9 Zum Weiterlesen
 

Zum Seitenanfang

9.5    Anonyme innere Klassen Zur vorigen ÜberschriftZur nächsten Überschrift

Anonyme Klassen gehen noch einen Schritt weiter als lokale Klassen: Auch sie sind innere Klassen, haben aber keinen Namen und erzeugen immer automatisch ein Objekt; Klassendeklaration und Objekterzeugung sind zu einem Sprachkonstrukt verbunden. Die allgemeine Notation ist folgende:

new KlasseOderSchnittstelle() { /* Eigenschaften der inneren Klasse */ }

In dem Block geschweifter Klammern lassen sich nun Methoden und Attribute deklarieren oder Methoden überschreiben. Hinter new steht der Name einer Klasse oder Schnittstelle:

  • new Klassenname(Optionale Argumente) { }: Steht hinter new ein Klassentyp, dann ist die anonyme Klasse eine Unterklasse von Klassenname. Es lassen sich mögliche Argumente für den Konstruktor der Basisklasse angeben (das ist zum Beispiel dann nötig, wenn die Oberklasse keinen parameterlosen Konstruktor deklariert).

  • new Schnittstellenname() { }: Steht hinter new der Name einer Schnittstelle, dann erbt die anonyme Klasse von Object und implementiert die Schnittstelle Schnittstellenname. Implementiert sie nicht die Operationen der Schnittstelle, ist das ein Fehler. Wir hätten nichts davon, denn dann hätten wir eine abstrakte innere Klasse, von der sich kein Objekt erzeugen lässt.

Für anonyme innere Klassen gilt die Einschränkung, dass keine zusätzlichen extends- oder implements-Angaben möglich sind. Ebenso sind keine eigenen Konstruktoren möglich (wohl aber Exemplarinitialisierer), und nur Objektmethoden und finale statische Variablen sind erlaubt.

 

Zum Seitenanfang

9.5.1    Nutzung einer anonymen inneren Klasse für den Timer Zur vorigen ÜberschriftZur nächsten Überschrift

In Listing 9.6 haben wir für den Timer extra eine neue lokale Klasse deklariert, aber genau genommen haben wir diese nur einmal nutzen müssen, nämlich um ein Exemplar zu bilden und scheduleAtFixedRate(…) übergeben zu können. Das ist ein perfektes Szenario für anonyme innere Klassen. Aus

class SportReminderTask extends TimerTask {

@Override public void run() { ... }

}

new Timer().scheduleAtFixedRate( new SportReminderTask(), ... );

wird:

Listing 9.7    src/main/java/com/tutego/insel/nested/ShorterSportReminder.java, main()

new Timer().scheduleAtFixedRate( new TimerTask() {

@Override public void run() {

System.out.println( "Los, ..." );

}

},

0 /* ms delay */,

1000 /* ms period */);

Im Kern ist es also eine Umwandlung von new SportReminderTask() in new TimerTask() { }. Von dem Klassennamen SportReminderTask ist nichts mehr zu sehen, das Objekt ist anonym.

[»]  Hinweis

Eine anonyme Klasse kann Methoden der Oberklasse überschreiben, Operationen aus Schnittstellen implementieren und sogar neue Eigenschaften anbieten:

Listing 9.8    src/main/java/com/tutego/insel/nested/ObjectWithQuote.java, main()

String s = new Object() {

String quote( String s ) {

return String.format( "'%s'", s );

}

}.quote( "Cora" );

System.out.println( s ); // 'Cora'

Der neu deklarierte anonyme Typ hat eine Methode quote(String), die direkt aufgerufen werden kann. Ohne diesen direkten Aufruf ist die quote(…)-Methode aber unsichtbar, denn der Typ ist ja anonym, und so sind nur die Methoden der Oberklasse (bei uns Object) bzw. der Schnittstelle bekannt. (Wir lassen die Tatsache außen vor, dass eine Anwendung mit Reflection auf die Methoden zugreifen kann.)

 

Zum Seitenanfang

9.5.2    Umsetzung innerer anonymer Klassen * Zur vorigen ÜberschriftZur nächsten Überschrift

Auch für innere anonyme Klassen erzeugt der Compiler eine normale Klassendatei. Wir haben gesehen, dass der Java-Compiler bei einer »normalen« geschachtelten Klasse die Notation ÄußereKlasse$InnereKlasse wählt. Das klappt bei anonymen inneren Klassen natürlich nicht mehr, da uns der Name der inneren Klasse fehlt. Der Compiler wählt daher folgende Notation für Klassennamen: InnerToStringDate$1. Falls es mehr als eine innere Klasse gibt, folgen $2, $3 usw.

Ausnahmen in inneren anonymen Klassen

In einem Stack-Trace taucht der generierte Klassenname auf, wenn es eine Ausnahme gab. Ist etwa die Deklaration eingebettet in eine main(…)-Methode der Klasse T:

new Object() { String nuro() { throw new IllegalStateException(); } }.nuro();

So folgt bei der Ausführung:

Exception in thread "main" java.lang.IllegalStateException

at T$1.nuro(T.java:6)

at T.main(T.java:6)
 

Zum Seitenanfang

9.5.3    Konstruktoren innerer anonymer Klassen Zur vorigen ÜberschriftZur nächsten Überschrift

Der Compiler setzt anonyme Klassen in normale Klassendateien um. Jede Klasse kann einen eigenen Konstruktor deklarieren, und auch für anonyme Klassen sollte das möglich sein, um Initialisierungscode dort hineinzusetzen.

Wir wollen eine innere Klasse schreiben, die eine Unterklasse von java.awt.Point ist. Sie soll die toString()-Methode überschreiben:

Listing 9.9    src/main/java/com/tutego/insel/nested/InnerToStringPoint.java, main()

Point p = new Point( 10, 12 ) {

@Override public String toString() {

return "(" + x + "," + y + ")";

}

};



System.out.println( p ); // (10,12)

Die anonyme Unterklasse wird also durch den normalen Konstruktor von Point initialisiert.

Exemplarinitialisierungsblöcke bei inneren anonymen Klassen

Da aber anonyme Klassen keinen Namen haben, muss für Konstruktoren ein anderer Weg gefunden werden. Hier helfen Exemplarinitialisierungsblöcke, also Blöcke in geschweiften Klammern direkt innerhalb einer Klasse, die wir schon in Kapitel 6, »Eigene Klassen schreiben«, vorgestellt haben. Exemplarinitialisierer gibt es ja eigentlich gar nicht im Bytecode, sondern der Compiler setzt den Programmcode automatisch in jeden Konstruktor. Obwohl anonyme Klassen keinen direkten Konstruktor haben können, gelangt doch über den Exemplarinitialisierer Programmcode in den Konstruktor der Bytecode-Datei.

Dazu ein Beispiel: Die anonyme Klasse ist eine Unterklasse von Point und initialisiert im Konstruktor einen Punkt mit Zufallskoordinaten. Aus diesem speziellen Punkt-Objekt lesen wir dann die Koordinaten wieder aus:

Listing 9.10    src/main/java/com/tutego/insel/nested/AnonymousAndInside.java, main()

java.awt.Point p = new java.awt.Point() {

{

x = (int)(Math.random() * 1000); y = (int)(Math.random() * 1000);

}

};



System.out.println( p.getLocation() ); // java.awt.Point[...



System.out.println( new java.awt.Point( -1, 0 ) {{

y = (int)(Math.random() * 1000);

}}.getLocation() ); // java.awt.Point[x=-1,y=...]
[»]  Sprachlichkeit

Wegen der beiden geschweiften Klammern heißt diese Variante auch Doppelklammer-Initialisierung (engl. double brace initialization).

Die Doppelklammer-Initialisierung ist kompakt, wenn etwa Datenstrukturen oder hierarchische Objekte initialisiert werden sollen.

[zB]  Beispiel *

Im folgenden Beispiel erwartet appendText(…) ein Objekt vom Typ HashMap, das durch den Trick direkt initialisiert wird:

String s = new DateTimeFormatterBuilder()

.appendText( ChronoField.AMPM_OF_DAY,

new HashMap<Long, String>() {{ put(0L, "früh");put(1L,"spät" ); }} )

.toFormatter().format( LocalTime.now() );

System.out.println( s );

Im nächsten Beispiel bauen wir eine geschachtelte Map – das ist ein Assoziativspeicher. Diese Map enthält an einem Punkt wieder einen anderen Assoziativspeicher:

Map<String,Object> map = new HashMap<String,Object>() {{

put( "name", "Chris" );

put( "address", new HashMap<String,Object>() {{

put( "street", "Feenallee 1" );

put( "city", "Elefenberg" );

}} );

}};
[ ! ]  Warnung

Die Doppelklammer-Initialisierung ist nicht ganz »billig«, da für die Unterklasse eine zusätzliche Klassendatei im Dateisystem generiert wird. Zudem hält die innere Klasse eine Referenz auf die äußere Klasse fest. Des Weiteren kann es Probleme mit equals(…) geben, da wir mit der Doppelklammer-Initialisierung eine Unterklasse schaffen, die vielleicht mit equals(…) nicht mehr gültig verglichen werden kann, denn die Class-Objekte sind jetzt nicht mehr identisch. Das spricht in der Summe eher gegen diese Konstruktion. Der Typ Map bietet mit den statischen of(…)/entry(…)-Methoden eine bessere Möglichkeit.

Gar nicht super() *

Innerhalb eines »anonymen Konstruktors« kann kein super(…) verwendet werden, um den Konstruktor der Oberklasse aufzurufen. Dies liegt daran, dass automatisch ein super(…) in den Initialisierungsblock eingesetzt wird. Die Parameter für die gewünschte Variante des (überladenen) Oberklassen-Konstruktors werden am Anfang der Deklaration der anonymen Klasse angegeben. Dies zeigt das folgende Beispiel:

System.out.println( new java.awt.Point( -1, 0 ) {{

y = (int)(Math.random() * 1000);

}}.getLocation() ); // java.awt.Point[x=-1,y=...]
[zB]  Beispiel

Wir initialisieren ein Objekt BigDecimal, das beliebig große Ganzzahlen aufnehmen kann. Im Konstruktor der anonymen Unterklasse geben wir anschließend den Wert mit der geerbten toString()-Methode aus:

new java.math.BigDecimal( "12345678901234567890" ) {{

System.out.println( toString() );

}};

 


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: Java SE 9 Standard-Bibliothek

Java SE 9 Standard-Bibliothek




Zum Rheinwerk-Shop: Algorithmen in Java

Algorithmen in Java




Zum Rheinwerk-Shop: Objektorientierte Programmierung

Objektorientierte Programmierung




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

InfoInfo



 

 


Copyright © Rheinwerk Verlag GmbH 2021

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