Verschachtelte Klassen und Verwandtes
in Arbeit ...
1. Überblick
Verschachtelte Klassen (nested classes) ermöglichen in Java das Einbetten einer Klasse in einer anderen.
Ihre Verwendung ist praktisch nie zwingend nötig. Allerdings helfen sie häufig, kompakteren, lesbareren, logischer strukturierten Code mit präziseren Sichtbarkeitsregeln zu schreiben.
-
Häufig werden Hilfsklassen, die logisch stark mit einer Klasse gekoppelt sind, am besten in diese eingebettet.
-
Hilfsklassen oder Datenklassen, die nur für den internen Gebrauch innerhalb einer anderen Klasse benötigt werden bzw. erlaubt sind, können so realisiert werden.
-
Oft wird nur an einer einzigen Stelle ein einziges Objekt einer Klasse benötigt (Comparatoren, Event-Handler in GUIs, …).
In Java gibt es mittlerweile 6 Arten verschachtelter Klassen oder konzeptuell verwandter Strukturen (aktuell wichtig für uns sind nur 3):
-
(wichtig) Statische verschachtelte Klasse (Static Nested Class) … logisch äquivalent zu normaler Klasse, nur syntaktisch eingebettet
-
Innere Klasse (Inner Class) … jede Instanz der inneren Klasse ist an eine Instanz der äußeren Klasse gebunden
-
"Normale" innere Klasse … Deklaration auf oberster Ebene (direkt innerhalb des Klassen-Rumpfes – "neben" Attributen und Methoden)
-
Lokale Klasse (Local Class) … Spezialfall Innere Klasse: "weiter innen" - z.B. innerhalb einer Methode.
-
(wichtig) Anonyme innere Klasse (anonymous inner class) … Lokale Klasse, wenn nur eine einzige Instanz an einer einzigen Stelle benötigt wird. In diesem Fall ist keine Name nötig. Beispiel: Comparator-Klasse
-
-
(wichtig) Lambda-Ausdruck … Weitere syntaktische Vereinfachung anonymer innerer Klasse (aber auch logische Adaptionen), ermöglicht extrem kompakte und (meist) übersichtlichere Formulierung z.B. der Logik einer compare-Methode.
-
Methoden-Referenzen … Nochmals syntaktische Vereinfachung, wenn im Lambda-Ausdruck lediglich ein einziger Aufruf einer bereits bestehenden Methode erfolgen würde.
Ein Beispiel für alle angeführten Fälle findet sich unten.
2. Statische verschachtelte Klasse
(für uns wichtig)
Diese wird beim Kompilieren zu einer eigenständigen Class-Datei (EnclosingClass$NestedClass.class).
Sie hat keinen speziellen Zugriff auf Elemente der umschließenden Klasse, verhält sich also bis auf den Namen wie jede andere Klasse auch.
3. Innere Klasse
(für uns derzeit nicht Thema)
Im Gegensatz zu obigen statisch verschachtelten Klassen sind Objekte der inneren Klasse jeweils an ein Objekt der umschließenden Klasse gebunden und kann auch auf dessen Attribute zugreifen!
Im Gegenzug ist also jeweils davor ein Objekt der umschließenden Klasse nötig. Dann muss die Instanzierung so erfolgen: InnerClass icObj = enclosingClassObject.new InnerClass();
Markant ist. dass new
mit '.' an ein äußeres Objekt geknüpft wird.
4. Lokale (innere) Klasse
(für uns derzeit nicht Thema)
siehe auch Local Classes (The Java™ Tutorials > Learning the Java Language > Classes and Objects)
5. Anonyme (innere) Klasse
(für uns wichtig)
Wenn ein einziges Objekt einer Klasse nur an einer Stelle benötigt wird, gibt es eine syntaktisch sehr kompakte Möglichkeit, die Klasse gleich "vor Ort" zu definieren und sofort ein Objekt daraus zu erzeugen. Die Klasse benötigt keinen (und hat auch keinen) Namen.
Anwendungsfälle sind z.B. das Sortieren oder (in Kürze) für JavaFX Ereignis-Reaktionen (Button-Klick etc.). Siehe Beispiel unten.
Siehe auch Jenkov.com: Java Nested Classes
6. Lambda-Ausdruck
(für uns wichtig)
Siehe auch Lambda Expressions (The Java™ Tutorials > Learning the Java Language > Classes and Objects)
7. Methoden-Referenzen
(für uns derzeit nicht Thema)
Siehe auch Method References (The Java™ Tutorials > Learning the Java Language > Classes and Objects) … 9.1.2022, 18:39:47
(Beispiel fehlt noch - siehe Link)
8. Beispiel-Klasse für alle 6 Konzepte
TODO: Beispiel Methodenreferenz fehlt noch!
package my.scribble2021q4a;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
public class EnclosingClass {
private String[] tokens = { "fff", "u", "bbb", "bb", "zzzz", "zz", "ff", "aaaa", "a" };
public EnclosingClass() {
System.out.println("now in constructor for object of DemoNestingOuterClasses ...");
}
public static void main(String[] args) {
EnclosingClass dnocObj = new EnclosingClass();
dnocObj.someMethod();
//
System.out.println("----- static nested classes -----");
StaticNestedClass dnocSncObj1 = new StaticNestedClass("dnocSncObj1"); // shorter, ok within nesting class
//From another class use EnclosingClass.StaticNestedClass
//
System.out.println("----- (ordinary) inner classes -----");
InnerClass icObj1 = dnocObj.new InnerClass("icObj1");
System.out.println("----- Method containing local classes -----");
dnocObj.methodWithLocalClass();
dnocObj.methodWithLambda();
}
public void someMethod() {
System.out.println("now in someMethod() ...");
}
public /* important--> */ static /* <-- */ class StaticNestedClass { // used in main(..)
private String title;
public StaticNestedClass(String title) {
System.out.format("now in constructor of StaticNestedClass-object '%s' ...%n", title);
}
}
public class InnerClass { // used in main(..)
public InnerClass(String title) {
System.out.format("now in constructor of InnerClass-object '%s' ...%n", title);
}
}
public void methodWithLocalClass() {
System.out.println("now in method 'methodWithLocalClass()'");
class LocalClass { // Start Implementation
public LocalClass() {
System.out.println("now in constructor of 'LocalClass'");
}
public void demoMethod1() {
System.out.println("now in method 'LocalClass#demoMethod1()'");
}
} // End Implementation
LocalClass lcObj1 = new LocalClass(); // Instantiate LocalClass
lcObj1.demoMethod1(); // use object of LocalClass (ALL WITHIN this single method!)
}
public void methodWithAnonymousInnerClass() {
System.out.println("now in method 'methodWithAnonymousInnerClass()'");
List<String> names = new ArrayList<>(Arrays.asList(tokens));
names.sort( // Start AnonymousInnerClass
new Comparator<String>() { // can extend class or implement interface
@Override
public int compare(String s1, String s2) {
int cmp = s1.length() - s2.length(); // first by length
if (cmp == 0) {
cmp = s1.compareTo(s2); // if same length: by ordinary string-compare
}
return cmp;
}
} // End AnonymousInnerClass
);
// the single param object is directly created with the 'new' before the implementation!
}
public void methodWithLambda() {
System.out.println("now in method 'methodWithLambda()'");
List<String> names = new ArrayList<>(Arrays.asList(tokens));
System.out.println("unsorted list: " + names);
names.sort( // Start LambdaExpression
(s1, s2) -> {
int cmp = s1.length() - s2.length(); // first by length
if (cmp == 0) {
cmp = s1.compareTo(s2); // if same length: by ordinary string-compare
}
return cmp;
} // End LambdaExpression
);
System.out.println("list sorted (length, then alphabetical): " + names);
}
//TODO: Method reference example
}