-
Notifications
You must be signed in to change notification settings - Fork 13
/
Innere-und-anonyme-Klassen.md
140 lines (98 loc) · 5.1 KB
/
Innere-und-anonyme-Klassen.md
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
# Innere und anonyme Klassen<!-- omit in toc -->
- [Innere Klassen](#innere-klassen)
- [Anonyme Klassen](#anonyme-klassen)
## Innere Klassen
> 💬 eng.: _inner class_ oder auch _nested class_
Als innere Klassen werden Klassen bezeichnet, die innerhalb anderer Klassen definiert sind. Dies wird u.a. dazu genutzt, den Coder besser zu strukturieren: Manchmal ist eine Klasse semantisch so sehr einer anderen Klasse untergeordnet und wäre alleine unbrauchbar, dass sie am besten nicht in einer eigenen Datei steht:
```java
class ContactData {
private String firstName;
private String lastName;
private Address address;
//...
class Address {
private String street;
private String city;
//...
}
}
```
> 💬 In diesem Beispiel geht es nur um die Semantik der verwendeten Attribute. In der Realität bräuchte man für die Adresse natürlich nicht unbedingt eine eigene Klasse!
In diesem Beispiel wird die Klasse `Address` eventuell sogar niemals außerhalb der Klasse `Customer` genutzt. Das wäre dann eigentlich ein Fall für eine `private` innere Klasse, denn innere Klassen können (anders als normale Klassen) `private` oder auch `protected` sein (siehe auch [Sichtbarkeitsmodifizierer](Objekte-I-Initialisierung-Members-Zugriff.md#zugriffs-sichetbarkeitsmodifizierer)).
Innere Klassen können aber auch "von außerhalb" genutzt werden:
```java
class Outer {
private String s = "something";
class Inner {
public void foo(){
System.out.println(s);
}
}
}
```
Von einer anderen Klasse aus ließe sich nun folgendes tun:
```java
public static void main(String[] args) {
Outer outer = new Outer();
Outer.Inner inner = outer.new Inner();
inner.foo();
}
```
Hier sehen wir gleich mehrere interessante Dinge:
1) Wir benötigen eine Instanz von `Outer`, um eine Instanz von `Inner` erzeugen zu können (es sei denn `Inner` ist `static` - denn auch das ist möglich!).
2) Von `Inner` aus kann auf Attribute und Methoden von `Outer` zugegriffen werden!
Wäre `Inner` eine statische innere Klasse ...
```java
class Outer {
static class Inner {
// ...
}
}
```
... dann wäre der Zugriff auf `Inner` auch ohne eine Instanz von `Outer` möglich (ähnlich wie bei statischen Methoden oder Klassenvariablen):
```java
Outer.Inner inner = new Outer.Inner();
```
> ⚠️ Natürlich kann eine statische innere Klasse auch nur auf statische 👉 [Member](../Glossar.md#member) der äußeren Klasse zugreifen!
> 🔗 Eine hübsche Übersicht zu inneren Klassen (mit praktischen Beispielen und Code zum Ausführen) findet man in [diesem W3Schools-Artikel](https://www.w3schools.com/java/java_inner_classes.asp).
## Anonyme Klassen
Eine anonyme Klasse trägt **keinen Namen** und wird in ein und dem selben Statement **deklariert und instanziiert**. Da in Java jedes Objekt aber eine klare Typ-Zuordnung braucht, muss diese anonyme Klasse entweder eine bestehende Klasse **erweitern** _oder_ ein Interface **implementieren**. Die Syntax ist in beiden Fällen gleich, da weder `extends` noch `implements` verwendet werden.
Nehmen wir folgende Klasse `Foo` an:
```java
public class Foo {
public void saySomething() {
System.out.println("foo");
}
}
```
Indem wir eine anonyme Klasse verwenden, können wir sehr schnell und ohne eine neue `.java` Datei anzulegen eine "Wegwerf"-Erweiterung von `Foo` mit überschriebener Methode `foo()` deklarieren und sofort eine Instanz davon erzeugen:
```java
// Instanz von "Foo" erzeugen
Foo foo = new Foo();
// Instanz von "Bar" (Erweiterung von "Foo") erzeugen
Foo bar = new Foo() {
@Override
public void saySomething() {
System.out.println("bar");
}
};
foo.saySomething();
bar.saySomething();
```
Die Ausgabe dieses Codes wäre - wenig überraschend:
```
foo
bar
```
Diese anonyme Klasse lässt sich (durch den fehlenden Namen) nur ein einziges mal instanziieren. Es sei denn, die Methode, in der das geschieht, wird mehrmals aufgerufen - aber in diesem Fall ließe sich wohl darüber diskutieren, ob es sich dann nicht eigentlich um einmalige Instanzen unterschiedlicher Klassen mit identischer Deklaration handelt.
> ⚠️ Wäre `Foo` in diesem Beispiel ein Interface, sähe der Code zur Deklaration der anonymen Klasse (die `Foo` _implementiert_) exakt genauso aus! Anonyme Klassen _erweitern_ oder _implementieren_ Klassen oder Interfaces also **implizit** - je nach dem, worum es sich im Einzelfall handelt.
Ein sehr häufiger Anwendungsfall für anonyme Klassen in Java sind 🔗 [Listener](https://de.wikipedia.org/wiki/Beobachter_(Entwurfsmuster)) für Buttons (o.ä.) in 👉 [GUI](../Glossar.md#gui)s, bei denen zum Zeitpunkt der Initialisierung noch schnell die Methode überschrieben werden soll, die ausgeführt wird, wenn das jeweilige Ereignis (Klick o.ä.) eingetreten ist. Das sieht dann z.B. so aus:
```java
button.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
button.setText("I was clicked!");
}
});
```
> 🔗 Eine gute weiterführende Ressource (in englischer Sprache) ist [dieser Artikel](https://www.baeldung.com/java-anonymous-classes).