From 33613a85afc4b1481367fbe92a17ee59c240250b Mon Sep 17 00:00:00 2001 From: Sven Eisenhauer Date: Fri, 10 Nov 2023 15:11:48 +0100 Subject: add new repo --- .../hjp5/html/k100065.html | 542 +++++++++++++++++++++ 1 file changed, 542 insertions(+) create mode 100644 Master/Reference Architectures and Patterns/hjp5/html/k100065.html (limited to 'Master/Reference Architectures and Patterns/hjp5/html/k100065.html') diff --git a/Master/Reference Architectures and Patterns/hjp5/html/k100065.html b/Master/Reference Architectures and Patterns/hjp5/html/k100065.html new file mode 100644 index 0000000..a1ceeb8 --- /dev/null +++ b/Master/Reference Architectures and Patterns/hjp5/html/k100065.html @@ -0,0 +1,542 @@ + + + +Handbuch der Java-Programmierung, 5. Auflage + + + + + + + + + +
 Titel  + Inhalt  + Suchen  + Index  + DOC  +Handbuch der Java-Programmierung, 5. Auflage +
 <<  +  <   +  >   + >>  + API  +Kapitel 10 - OOP IV: Verschiedenes +
+
+ + + + +

10.1 Lokale und anonyme Klassen

+
+ +
+ + + + +

10.1.1 Grundlagen

+ +

+Bis zum JDK 1.0 wurden Klassen immer auf Paketebene definiert, eine +Schachtelung war nicht möglich. Das hat die Compiler-Implementierung +vereinfacht und die Struktur der Klassen innerhalb eines Pakets flach +und übersichtlich gemacht. Unhandlich wurde dieses Konzept immer +dann, wenn eine Klasse nur lokale Bedeutung hatte oder wenn »auf +die Schnelle eine kleine Klasse« gebraucht wurde. Da es in Java +keine Funktionszeiger gibt, besteht die einzige Möglichkeit, +kleine Codebestandteile zwischen verschiedenen Programmteilen auszutauschen, +darin, ein Interface zu deklarieren und die benötigte Funktionalität +in einer implementierenden Klasse zur Verfügung zu stellen. Diese +Technik wurde in Abschnitt 9.4.3 +vorgestellt. + +

+Insbesondere bei den Erweiterungen für die Programmierung grafischer +Oberflächen, die mit dem JDK 1.1 eingeführt wurden, entstand +der Wunsch nach einem flexibleren Mechanismus. Durch das neue Ereignismodell +(siehe Kapitel 28) +müssen seit dem JDK 1.1 sehr viel häufiger kleine Programmteile +geschrieben werden, die nur in einem eng begrenzten Kontext eine Bedeutung +haben. Die Lösung für dieses Problem wurde mit der Einführung +von lokalen und anonymen Klassen geschaffen (im JDK werden sie als +Inner Classes bezeichnet). Dabei wird +innerhalb einer bestehenden Klasse X eine neue Klasse Y definiert, +die nur innerhalb von X sichtbar ist. Objektinstanzen von Y können +damit auch nur innerhalb von X erzeugt werden. Anders herum kann Y +auf die Membervariablen von X zugreifen. + +

+Lokale und anonyme Klassen sind ein mächtiges - und manchmal +leicht verwirrendes - Feature von Java. Wir wollen nachfolgend seine +wichtigsten Anwendungen vorstellen. Darüber hinaus gibt es seltener +genutzte Varianten, die hauptsächlich durch trickreiche Anwendung +von Modifiern auf die lokale Klasse oder ihrer Member entstehen. Auf +diese wollen wir hier nicht weiter eingehen. + + + + +

10.1.2 Nicht-statische lokale Klassen

+ + + + +

Klassen in Klassen

+ +

+Die Definition einer nicht-statischen lokalen Klasse entspricht genau +dem zuvor beschriebenen Grundprinzip: innerhalb des Definitionsteils +einer beliebigen Klasse wird eine neue Klasse definiert. Ihre Instanzierung +muss innerhalb der äußeren Klasse erfolgen, also in einer +der Methoden der äußeren Klasse oder während ihrer +Initialisierung. Die innere Klasse kann auf die Membervariablen der +äußeren Klasse zugreifen und umgekehrt. Das folgende Listing +illustriert dies an einem einfachen Beispiel: + + +

+ + + + + +
+ +
+001 /* Listing1001.java */
+002 
+003 class Outer
+004 {
+005   String name;
+006   int    number;
+007 
+008   public void createAndPrintInner(String iname)
+009   {
+010     Inner inner = new Inner();
+011     inner.name = iname;
+012     System.out.println(inner.getQualifiedName());
+013   }
+014 
+015   class Inner
+016   {
+017     private String name;
+018 
+019     private String getQualifiedName()
+020     {
+021       return number + ":" + Outer.this.name + "." + name;
+022     }
+023   }
+024 }
+025 
+026 public class Listing1001
+027 {
+028   public static void main(String[] args)
+029   {
+030     Outer outer = new Outer(); 
+031     outer.name = "Outer";
+032     outer.number = 77;
+033     outer.createAndPrintInner("Inner");
+034   }
+035 }
+
+
+Listing1001.java
+ +Listing 10.1: Eine nicht-statische lokale Klasse

+ +

+Zunächst wird eine Klasse Outer +mit den Membervariablen name +und number definiert. Innerhalb +von Outer wird dann eine Klasse +Inner definiert. Sie besitzt +eine eigene Membervariable name +und eine Methode getQualifiedName. +Beim Programmstart erzeugt main +eine Instanz von Outer und initialisiert +ihre Membervariablen. Anschließend ruft sie die Methode createAndPrintInner +auf. + +

+In createAndPrintInner wird +nun eine Instanz von Inner erzeugt +und mit dem als Argument übergebenen Namen initialisiert. Die +Instanzierung erfolgt also im Kontext der äußeren Klasse, +und diese kann auf die Membervariable der inneren Klasse zugreifen. +In der Praxis wichtiger ist jedoch die Möglichkeit, die innere +Klasse auf die Membervariablen der äußeren Klasse zugreifen +zu lassen. Dadurch wird ihr der Status der äußeren Klasse +zugänglich gemacht und sie kann Programmcode erzeugen (und durch +die Kapselung in ein Objekt nötigenfalls an eine ganz andere +Stelle im Programm transferieren), der Informationen aus der Umgebung +der Klassendefinition verwendet. Um dies zu zeigen, ruft Outer +nun die Methode getQualifiedName +der inneren Klasse auf. + +

+In getQualifiedName wird auf +drei unterschiedliche Arten auf Membervariablen zugegriffen. Bei der +Verwendung von unqualifizierten Namen (also solchen ohne Klassennamen-Präfix) +werden lexikalische Sichtbarkeitsregeln angewandt. Der Compiler +prüft also zunächst, ob es eine lokale Variable dieses Namens +gibt. Ist das nicht der Fall, sucht er nach einer gleichnamige Membervariable +in der aktuellen Klasse. Ist auch diese nicht vorhanden, erweitert +er seine Suche sukzessive von innen nach außen auf alle umschließenden +Klassen. Im Beispiel bezeichnet name +also die gleichnamige Membervariable von Inner +und number diejenige von Outer. +Wird die Membervariable einer äußeren Klasse durch eine +gleichnamige Membervariable der inneren Klasse verdeckt, kann über +den Präfix »Klassenname.this.« auf die äußere +Variable zugegriffen werden. Im Beispielprogramm wird das für +die Variable name so gemacht. + +

+Wird der Ausdruck »Klassenname.this« alleine verwendet, +bezeichnet er das Objekt der äußeren Klasse, in der die +aktuelle Instanz der inneren Klasse erzeugt wurde. In getQualifiedName +würde Outer.this also die +in Zeile 030 erzeugte +Instanz outer bezeichnen. +

+ + + + + + + + + +
+ +

+Die Implementierung von lokalen Klassen konnte im JDK 1.1 ohne größere +Änderungen der virtuellen Maschine vorgenommen werden. Lokale +Klassen sind zwar zum Compilezeitpunkt bekannt, werden aber zur Laufzeit +behandelt wie normale Klassen. Insbesondere wird vom Compiler zu jeder +lokalen Klasse eine eigene .class-Datei +erzeugt. Um Überschneidungen zu vermeiden, besteht ihr Name aus +dem Namen der äußeren Klasse, gefolgt von einem Dollarzeichen +und dem Namen der inneren Klasse. Bei den später behandelten +anonymen Klassen wird statt des Namens der inneren Klasse eine vom +Compiler vergebene fortlaufende Nummer verwendet. Beim Übersetzen +des vorigen Beispiels würden also die Klassendateien Outer.class, +Outer$Inner.class und Listing1001.class +generiert.

+ + + + +
 Hinweis 
+
+ + + + +

Klassen in Methoden

+ +

+Innere Klassen können nicht nur auf der äußersten +Ebene einer anderen Klasse definiert werden, sondern auch innerhalb +ihrer Methoden und sogar innerhalb eines beliebigen Blocks. In diesem +Fall können sie auch auf die lokalen Variablen der umgebenden +Methode oder des umgebenden Blocks zugreifen. Bedingung ist allerdings, +dass diese mit Hilfe des Schlüsselworts final +als konstant deklariert wurden. + +

+Diese Art, lokale Klassen zu definieren, ist nicht sehr gebräuchlich, +taucht aber mitunter in fremdem Programmcode auf. In der Praxis werden +an ihrer Stelle meist anonyme Klassen verwendet, wie sie im folgenden +Abschnitt besprochen werden. Wir wollen uns dennoch ein einfaches +Beispiel ansehen: + + +

+ + + + + +
+ +
+001 /* Listing1002.java */
+002 
+003 class Outer2
+004 {
+005   public void print()
+006   {
+007     final int value = 10;
+008 
+009     class Inner2
+010     {
+011       public void print()
+012       {
+013         System.out.println("value = " + value);
+014       }
+015     }
+016 
+017     Inner2 inner = new Inner2();
+018     inner.print();
+019   }
+020 }
+021 
+022 public class Listing1002
+023 {
+024   public static void main(String[] args)
+025   {
+026     Outer2 outer = new Outer2();
+027     outer.print();
+028   }
+029 }
+
+
+Listing1002.java
+ +Listing 10.2: Definition einer lokalen Klasse in einer Methode

+ + + + +

10.1.3 Anonyme Klassen

+ +

+Die häufigste Anwendung lokaler Klassen innerhalb von Methoden +besteht darin, diese anonym zu definieren. Dabei erhält +die Klasse keinen eigenen Namen, sondern Definition und Instanzierung +erfolgen in einer kombinierten Anweisung. Eine anonyme Klasse ist +also eine Einwegklasse, die nur einmal instanziert werden kann. Anonyme +Klassen werden normalerweise aus anderen Klassen abgeleitet oder erweitern +existierende Interfaces. Ihre wichtigste Anwendung finden sie bei +der Definition von Listenern für grafische Oberflächen, +auf die wir in Kapitel 28 +noch ausführlich eingehen werden. + +

+Als einfaches Anwendungsbeispiel wollen wir das in Listing 9.14 +definierte Interface DoubleMethod +noch einmal verwenden und zeigen, wie man beim Aufruf von printTable +eine anonyme Klasse erzeugt und als Argument weitergibt: + + +

+ + + + + +
+ +
+001 /* Listing1003.java */
+002 
+003 public class Listing1003
+004 {
+005   public static void printTable(DoubleMethod meth)
+006   {
+007     System.out.println("Wertetabelle " + meth.toString());
+008     for (double x = 0.0; x <= 5.0; x += 1) {
+009       System.out.println(" " + x + "->" + meth.compute(x));
+010     }
+011   }
+012 
+013   public static void main(String[] args)
+014   {
+015     printTable(
+016       new DoubleMethod() 
+017       {
+018         public double compute(double value)
+019         {
+020           return Math.sqrt(value);
+021         }
+022       }
+023     );
+024   }
+025 }
+
+
+Listing1003.java
+ +Listing 10.3: Anwendung anonymer Klassen

+ +

+Statt eine vordefinierte Klasse zu instanzieren, wird hier in Zeile 016 +eine lokale anonyme Klasse definiert und instanziert. Syntaktisch +unterscheidet sie sich von der Instanzierung einer vordefinierten +Klasse dadurch, dass nach dem new KlassenName(...) +nicht ein Semikolon, sondern eine geschweifte Klammer steht. Anschließend +folgt die Definition der Klasse. Wird als Klassenname ein Interface +angegeben, implementiert die anonyme Klasse dieses Interface. Handelt +es sich dagegen um den Namen einer Klasse, wird die anonyme +Klasse daraus abgeleitet. In unserem Beispiel wird das Interface DoubleMethod +implementiert. +

+ + + + + + + + + +
+ +

+Das Programm hat durch die Verwendung der anonymen Klasse nicht unbedingt +an Übersichtlichkeit gewonnen. Tatsächlich sind sowohl Nutzen +als auch Syntax anonymer Klassen Gegenstand vieler Diskussionen gewesen. +Der große Vorteil anonymer Klassen besteht in ihrer Flexibilität. +Eine anonyme Klasse kann da deklariert werden, wo sie gebraucht wird +(hier beispielsweise beim Aufruf von printTable). +Zudem kann sie Code weitergeben, der auf lokale Variablen und Membervariablen +ihrer unmittelbaren Umgebung zugreift. + +

+Ihre Anwendung sollte sich auf die Fälle beschränken, in +denen eine Klasse mit wenigen Zeilen Code erzeugt werden muss, die +nur an einer bestimmten Programmstelle bedeutsam ist. Ist die Klasse +umfangreicher oder wird sie an verschiedenen Stellen benötigt, +sollte eine benannte Klasse definiert und an den Aufrufstellen instanziert +werden.

+ + + + +
 Hinweis 
+
+ + + + +

10.1.4 Statische lokale Klassen

+ +

+Die letzte Variante innerer Klassen, die wir betrachten wollen, ist +eigentlich gar keine. Hierbei wird eine Klasse innerhalb einer anderen +Klasse definiert und mit dem Attribut static +versehen. In diesem Fall erzeugt der Compiler Code, der genau dem +einer gewöhnlichen globalen Klasse entspricht. Insbesondere ist +eine statische lokale Klasse nicht nur innerhalb der Klasse sichtbar, +in der sie definiert wurde, sondern kann auch von außen instanziert +werden. Sie hält auch keinen Verweis auf die instanzierende Klasse +und kann somit nicht auf deren Membervariablen zugreifen. Der einzige +Unterschied zu einer globalen Klasse besteht darin, dass der Name +der inneren Klasse als Präfix den Namen der äußeren +Klasse enthält. Beide sind durch einen Punkt voneinander getrennt. + +

+Eine Klasse könnte beispielsweise dann als statische lokale Klasse +definiert werden, wenn ihre Daseinsberechtigung auf der Existenz der +äußeren Klasse basiert. Typische Anwendungen sind kleinere +Hilfsklassen, die ausreichend Substanz zur Deklaration einer eigenen +Klasse, aber zu wenig für eine eigene Datei haben. Durch den +separaten Namensraum können sie auch Allerweltsnamen wie »Entry«, +»Element« oder »Debug« haben. + +

+Das folgende Listing zeigt eine einfache Anwendung statischer lokaler +Klassen: + + +

+ + + + + +
+ +
+001 /* Listing1004.java */
+002 
+003 class Outer3
+004 {
+005   static class Inner3
+006   {
+007     public void print()
+008     {
+009       System.out.println("Inner3");
+010     }
+011   }
+012 }
+013 
+014 public class Listing1004
+015 {
+016   public static void main(String[] args)
+017   {
+018     Outer3.Inner3 inner = new Outer3.Inner3();
+019     inner.print();
+020   }
+021 }
+
+
+Listing1004.java
+ +Listing 10.4: Anwendung statischer lokaler Klassen

+

+ + + + + + + + + +
+ +

+Lokale und anonyme Klassen werden recht häufig eingesetzt. Auch +in diesem Buch sind weitere Beispiele für ihre Anwendung zu finden. +So wird beispielsweise in Abschnitt 28.2.2 +erläutert, wie man sie zur Entwicklung von Ereignishandlern für +GUI-Programme nutzen kann.

+ + + + +
 Hinweis 
+
+


+ + + +
 Titel  + Inhalt  + Suchen  + Index  + DOC  +Handbuch der Java-Programmierung, 5. Auflage, Addison +Wesley, Version 5.0.1 +
 <<  +  <   +  >   + >>  + API  +© 1998, 2007 Guido Krüger & Thomas +Stark, http://www.javabuch.de +
+ + + -- cgit v1.2.3