Info B VL 10: Innere Klassen Objektorientiere Programmierung in Java 2003 Ute Schmid (Vorlesung) Elmar Ludwig (Übung) FB Mathematik/Informatik, Universität Osnabrück Info B VL 10: Innere Klassen – p.221 Innere Klassen Bisher: “top-level” Klassen (direkte Mitglieder eines Pakets) Seit Java 1.1: Innere Klassen: definiert innerhalb einer anderen Klasse (Komponente einer Klasse, ähnlich Felder und Methoden) Vier Arten von inneren Klassen: Member Classes (“echte” innere Klasse) Static Member Classes (Nested Top-Level Classes) Local Classes Anonymous Classes Info B VL 10: Innere Klassen – p.222 Member Klassen / Beispiel Ein Auto besteht aus vielen Teilen – Motor, Gangschaltung, Auspuff, etc. Manche Teile bilden sinnvollerweise eine eigene Klasse, aber können dennoch nicht unabhängig vom Auto existieren. Beispiel: Klimaanlage Interaktion zwischen Klimaanlage und Auto ist notwendig. Leistung der Klimaanlage ist abhängig von Geschwindigkeit des Autos. Je langsamer das Auto fährt, desto mehr Energie muss die Klimaanlage zum Kühlen aufbringen. Lösung: Member Klasse Info B VL 10: Innere Klassen – p.223 ‘AutoAirCondition’ class AirConditioner { ... public float getTemperatureMin() { }; public float getTemperatureMax() { }; } class Automobile { private Engine engine; private GearBox gearBox; ... private class AutoAirConditioner extends AirConditioner { private float default = ...; private float factor = ...; ... public float getTargetTemperature () { float temperature = default - factor * engine.getSpeed(); ... } } public AirConditioner getAirConditioner () { return new AutoAirConditioner(); } } Info B VL 10: Innere Klassen – p.224 ‘AirConditioner’ – Erläuterung (1) Der AutoAirConditioner ist abhängig von Parametern eines bestimmten Autos Beachte: Ein Objekt der inneren Klasse kann nur zusammen mit einem Objekt der umschließenden Klasse existieren Erst neues Automobil erzeugen, dann die AutoAirCondition! Es ist nicht möglich, ein Objekt vom Typ AutoAirCondition zu erzeugen, ohne dass ein Auto, zu dem diese Klimaanlage gehört existiert. Info B VL 10: Innere Klassen – p.225 ‘AirConditioner’ – Erläuterung (2) Innere Klasse ist privat: Andere Klassen/Objekte können nur auf das öffentliche Inferface (AirConditioner) zugreifen. Die umschließende Klasse hat eine Methode, um ein “Handle” (Referenz) auf die öffentlichen Teile des Objekts der inneren Klasse zu liefern (getAirConditioner). Beachte: return new AutoAirConditioner() ist explizit: return this.new AutoAirConditioner() Alternatives Beispiel: Organe können nicht ohne Körper existieren. Weitere Beispiele: Enumerator (bzw. Iterator) Im Kontext von Auto: z.B. Aufzählen aller Schrauben, um z.B. mittlere Größe, mittlere Kosten zu bestimmen. Info B VL 10: Innere Klassen – p.226 Eigenschaften von Member Klassen (1) Member-Klassen sind die typischen, “echten” inneren Klassen. Member-Klassen sind wie Instanz-Felder und -Methoden mit einer Instanz der Klasse, in der sie definiert sind, assoziiert. Also: Zugriff auf alle Komponenten der umschliessenden Klasse. Member-Klassen können beliebig tief geschachtelt werden. D. h., eine innere Klasse kann weitere innere Klassen enthalten. Eine Member-Klasse kann mit allen Sichtbarkeits-Modifikatoren deklariert werden. Name muss verschieden vom Namen der umschliessenden Klasse sein. Info B VL 10: Innere Klassen – p.227 Eigenschaften von Member Klassen (2) Member-Klassen dürfen keine statischen Komponenten enthalten. Ausnahme: static und final deklarierte Konstanten. Interfaces können nicht als Member-Klassen definiert werden, da Interfaces keine Instanz-Variablen besitzen dürfen (also kein this-Verweis möglich). Info B VL 10: Innere Klassen – p.228 Eigenschaften von Member Klassen (3) Wichtigstes Merkmal: Zugriff auf Instanz-Felder und -Methoden der umschliessenden Klasse. current < numOfEls Wie funktioniert explizite Referenz? this.current < this.numOfEls Problem: this.numOfEls ist nicht zulässig (this bezieht sich auf Enumerator-Objekt) Erweiterte Syntax: this.current < MyListMC.this.numOfEls Diese Zugriffsform ist dann notwendig, wenn man sich auf eine Komponente einer äusseren Klasse beziehen will, die denselben Namen hat wie eine Komponente der inneren Klasse. Info B VL 10: Innere Klassen – p.229 Eigenschaften von Member Klassen (4) Analoge Erweiterung der super-Syntax (Zugriff auf eine überdeckte oder überschriebene Komponente der Oberklasse der umschliessenden Klasse): Klassenname .super. feld Klassenname .super. methode Ausführung des Member-Klassen Konstruktors bewirkt, dass die neue Instanz mit dem this Objekt der umschliessenden Klasse assoziiert wird. Gleichbedeutende Schreibweisen: return new public Enumeration enumerate() Enumerator(); public Enumeration enumerate() Enumerator(); return this.new Info B VL 10: Innere Klassen – p.230 Eigenschaften von Member Klassen (5) Anstelle der Definition von enumerator() könnte eine Enumeration auch so erzeugt werden: MyListMC intlist = new MyListMC(); // Create empty list Enumeration enum = intlist.new Enumerator(); // Create Enum for it Da die umschliessende Instanz implizit den Namen der umschliessenden Klasse spezifiziert, ist die explizite Angabe der Klasse ein Syntaxfehler: Enumeration e = intlist.new MyListMC.Enumerator(); // Syntax error Info B VL 10: Innere Klassen – p.231 Implementation von Member-Klassen (1) Erweiterung der Sprache (“syntactic sugar”) aber nicht der JVM: Java Compiler wandelt Repräsentation von inneren Klassen entsprechend um. Compilation in eigene top-level-Datei. Compiler muss Code so manipulieren, dass Zugriff auf Komponenten zwischen innerer und äusserer Klasse funktioniert. this$0 Feld für jede Member-Klasse (Assoziation mit Instanz der umschliessenden Klasse; Abspeichern der entsprechenden Referenz). Für weitere Referenzen zu umschliessenden Klassen wird entsprechend weitergezählt (this$1, etc.). Info B VL 10: Innere Klassen – p.232 Implementation von Member-Klassen (2) Jeder Member-Klassen Konstruktor erhält einen zusätzlichen Parameter, um dieses Feld zu initialisieren. protected Member-Klassen werden public; private Member-Klassen werden default-sichtbar. Info B VL 10: Innere Klassen – p.233 Member-Klassen und Vererbung Es ist erlaubt, dass eine top-level Klasse als Unterklasse einer Member-Klasse definiert wird. Damit hat die Unterklasse keine umschliessende Klasse, aber ihre Oberklase! Wegen unklarer Semantik argumentieren einige dafür, dass diese Art der Vererbung verboten werden soll. Atsushi Igarashi and Benjamin C. Pierce (2001). On inner classes. Information and Control. Die Autoren haben bei der Definition einer Reduktions-Semantik für innere Klassen und Vererbung Unterspezifikationen der Sprache Java aufgedeckt. Info B VL 10: Innere Klassen – p.234 Beispiel 1 // A top-level class that extends a member class class SpecialEnumerator extends MyListMC.Enumerator { // The constructor must explicitely specify a // containing instance // when invoking the superclass constructor public SpecialEnumerator(MyListMC l) { l.super(); } // Rest of class omitted } Info B VL 10: Innere Klassen – p.235 Beispiel 2 (Igarashi and Pierce, 2001) class C { void who(){ System.out.println("I’m a C object"); } class D extends C{ void m(){ C.this.who(); } void who(){ System.out.println("I’m a C.D object"); } } public static void main(String[] args){ new C().new D().m(); } } Info B VL 10: Innere Klassen – p.236 Vererbung und Namenskonflikte Zwei hierarchische Strukturen: Klassenhierarchie und Enthaltensein-Hierarchie (Containment) Es können Namenskonflikte zwischen vererbten Komponenten (Oberklasse) und Komponenten der umschliessenden Klasse auftreten. class A { int x; } class B { int x; class C extends A{ x; // inherited field this.x; // inherited field B.this.x; // field of containing class } } Info B VL 10: Innere Klassen – p.237 Static Member Classes / Beispiel Ein Autoradio gehört als Teil zum Auto, seine Eigenschaften sind aber unabhängig vom Auto selbst. Autoradios sind Objekte, die unabhängig von einem konkreten Auto existieren können: diese Klassen benötigen keinen Zugriff auf Instanz-Felder und/oder -Methoden der Automobil-Klasse. Statische innere Klassen dienen vor allem der Strukturierung von Programmcode. Info B VL 10: Innere Klassen – p.238 ‘AutoRadio’ interface Radio { void setVolume (); ... } class Automobile { private Engine engine; private GearBox gearBox; ..... private static class AutoRadio extends Radio { private int channel = ...; private float volume = ...; ... public void setVolume () { ... } } public Radio getRadio () { return new AutoRadio(); } } Info B VL 10: Innere Klassen – p.239 Eigenschaften von Static Members (1) Während Member-Klassen analog zu Instanz-Feldern und -Methoden zu sehen sind, sind static member classes ähnlich wie Klassen-Felder und -Methoden zu verstehen. (“class class”) Sie haben Zugriff auf alle statischen Komponenten der umschliessenden Klasse. Static member classes werden auch als nested top-level classes bezeichnet. Interfaces dürfen nur als static members definiert werden. Static Klassen können in einem Interface deklariert werden. Info B VL 10: Innere Klassen – p.240 Eigenschaften von Static Members (2) Static member classes (und Interfaces) werden wie top-level Klassen behandelt. Sie sind nicht mit einer Instanz der umschliessenden Klasse assoziiert (also: kein umschliessendes this-Objekt). Deklaration mit Zugriffsmodifikator genau wie für andere Komponenten. Name muss verschieden vom Namen der umschliessenden Klasse sein. (unqualifizierter) Zugriff auf alle (auch privaten) statischen Komponenten der umschliessenden Klasse (inklusiver weiterer static member classes). Methoden der umschliessenden Klasse haben Zugriff auf alle Komponenten der Member-Klasse. Info B VL 10: Innere Klassen – p.241 Eigenschaften von Static Members (3) Zugriff von externen Klassen: mit qualifiziertem Namen. Automobile.AutoRadio Vorteil: Strukturierung, paket-ähnliche Organisation für Klassen innerhalb einer Datei. Info B VL 10: Innere Klassen – p.242 Nutzung Innerer Klassen Member-Klassen (“echte” wie static deklarierte) sollen immer dann verwendet werden, wenn eine Referenz “von innen nach aussen” benötigt wird. Auf der Modellierungsebene heisst das: Ein Objekt ist aus anderen (inneren) Objekten aufgebaut, es kann nicht ohne diese inneren Objekte existieren, und die inneren Objekte benötigen Information über das umschliessende Objekt. Bei Member-Klassen werden Informationen der umschliessenden Instanz benötigt. Bei static deklarierten inneren Klassen werden statische bzw. keine Komponenten der umschliessenden Klasse benötigt, aber die umschliessene Klasse kann auf Komponenten der inneren Klasse zugreifen. Info B VL 10: Innere Klassen – p.243 Implementation von statischen Members Compiler generiert zwei Klassen-Dateien, z.B. Automobile.class und Automobile$AutoRadio.class (Innere Klasse AutoRadio wird zu top-level Klasse). Compiler qualifiziert Ausdrücke, die auf statische Komponenten der umschliessenden Klasse zugreifen, mit dem Klassennamen. Da auch auf private Komponenten zugegriffen werden darf: Automatische Generierung von nicht-privaten Zugriffsmethoden (mit Default-Zugriffsrechten, paket-weit) und Umwandlung der entsprechenden Ausdrücke. Info B VL 10: Innere Klassen – p.244 Lokale Klassen / Beispiel Alternative Modellierungsidee für die Automobil-Klasse Da die Klasse AutoAirConditioner nur einmal, innerhalb der Methode getAutoAirConditioner(), benötigt wird, kann die Klasse lokal definiert werden. siehe auch Enumerator-Beispiel: MyListLC.java Info B VL 10: Innere Klassen – p.245 ‘AutoAirConditioner’ class AirConditioner { ... public float getTemperatureMin() { }; public float getTemperatureMax() { }; } class Automobile { private Engine engine; private GearBox gearBox; ... public AirConditioner getAirConditioner () { class AutoAirConditioner extends AirConditioner { private float default = ...; private float factor = ...; ... public float getTargetTemperature () { float temperature = default - factor * engine.getSpeed(); } } return new AutoAirConditioner(); } } Info B VL 10: Innere Klassen – p.246 Eigenschaften Lokaler Klassen (1) Nicht Komponente einer Klasse, sondern innerhalb eines Blocks definiert. Typischerweise innerhalb einer Methode, auch innerhalb von Initialisierungsblöcken. Analogie: Lokale Variable – lokale Klasse; Instanz-Feld – Member-Klasse Geltungsbereich: Innerhalb des Blocks Java ist eine lexically scoped Sprache: Geltungsbereich von Variablen ist durch ihre Position im Code definiert: innerhalb der geschweiften Klammern, in die sie eingeschlossen sind. Wenn eine Member-Klasse nur innerhalb einer einzigen Methode der umschliessenden Klasse genutzt wird, kann sie ebenso gut als lokale Klasse definiert werden. Info B VL 10: Innere Klassen – p.247 Eigenschaften Lokaler Klassen (2) Name muss verschieden vom Namen der umschliessenden Klasse sein. Interfaces können nicht lokal deklariert werden. Wie Member-Klassen: Zugriff auf alle Komponenten der umschliessenden Klasse. Zusätzlich auf alle im Block sichtbaren final Parameter und Variablen. Keine Sichtbarkeits-Modifikatoren erlaubt. Info B VL 10: Innere Klassen – p.248 Eigenschaften Lokaler Klassen (3) Keine statischen Felder erlaubt (Ausnahme: static und final deklarierte Konstanten. (wie Member-Klassen) Zugriff auf sichtbare Variablen und Parameter nur, wenn diese final deklariert sind, weil die Lebensdauer einer Instanz einer lokalen Klasse länger sein kann als die Ausführung der Methode, in der sie definiert ist. D.h., die lokale Klasse benötigt eine private Kopie aller lokalen Variablen, die sie verwendet (automatisch vom Compiler erzeugt). Einzige Möglichkeit, Konsistenz zu garantieren (lokale Variablen und deren Kopie bleiben identisch): final. Info B VL 10: Innere Klassen – p.249 public class A { int f() { int i = 5; class B { int j = i; } i = i - 1; return new B().j; } public static void main (String[] args) { A a = new A(); int value1 = a.f; int value2 = a.f; // should be same value ! } } Info B VL 10: Innere Klassen – p.250 Eigenschaften Lokaler Klassen (4) Erweiterung der Java-Syntax: final-Modifikator darf nicht nur für lokale Variablen, sondern auch für Parameter von Methoden und Exception Parameter im catch-Statement angegeben werden. Wie Member-Klassen haben lokale Klassen Zugriff auf die Instanz der umschliessenden Klasse, falls sie nicht in einer Klassenmethode vereinbart werden (qualifiziertes this um auf Komponenten der umschliessenden Klasse zuzugreifen). Info B VL 10: Innere Klassen – p.251 Geltungsbereich Lokaler Klassen class A { protected char a = ’a’; } class B { protected char b = ’b’; } public class C extends A { private char c = ’c’; // visible to local class public static char d = ’d’; public void createLocalObject(final char e) { final char f = ’f’; int i = 0; // not final, not usable by local class class Local extends B { char g = ’g’; public void printVars(){ System.out.println(g); // this.g, field of Local System.out.println(f); // final local variable System.out.println(e); // final local parameter System.out.println(d); // C.this.d, field of containing class System.out.println(c); // C.this.c System.out.println(b); // inherited by Local System.out.println(a); // inherited by containing class } } Local l = new Local(); // Create instance of Local l.printVars(); // call its method } } Info B VL 10: Innere Klassen – p.252 Geltungsbereich vs. Objekt-Lebenszeit public class Weird { // A static member interface used below public static interface IntHolder { public int getValue(); } public static void main(String[] args) { IntHolder[] holders = new IntHolder[10]; // array to hold 10 objs for (int i = 0; i < 10; i++) { // Loop to fill array final int fi = i; // new for each iteration class MyIntHolder implements IntHolder { public int getValue() { return fi; } } holders[i] = new MyIntHolder(); // Instantiate local class } for(int i = 0; i < 10; i++) System.out.println(holders[i].getValue()); } } Info B VL 10: Innere Klassen – p.253