CodeGym /Kurse /JAVA 25 SELF /Innere Klassen (non-static inner)

Innere Klassen (non-static inner)

JAVA 25 SELF
Level 16, Lektion 0
Verfügbar

1. Was ist eine innere Klasse (inner class)?

In Java kann eine Klasse nicht nur auf oberster Ebene (in der Datei), sondern auch innerhalb einer anderen Klasse deklariert werden. Eine solche Klasse wird geschachtelte Klasse (nested class) genannt. Wenn eine solche Klasse ohne den Modifizierer static deklariert ist, heißt sie innere Klasse (oder non-static inner class, bzw. einfach inner class).

Eine innere Klasse ist eine Klasse, die innerhalb einer anderen Klasse deklariert ist und mit einer Instanz dieser äußeren Klasse verknüpft ist. Sie kann auf alle Felder und Methoden der äußeren Klasse zugreifen, sogar wenn sie private sind. Es ist, als hätte ein Objekt seinen „geheimen Helfer“, der alles darf!

Das ist etwa wie ein Haus mit Zimmern. Das Haus ist die äußere Klasse, das Zimmer die innere Klasse. Ein Zimmer kann ohne das Haus nicht existieren, hat aber Zugang zu dessen Ressourcen: Licht, Heizung, Möbel. Wenn das Haus verschwindet, verschwindet auch das Zimmer. Genauso funktionieren innere Klassen in Java!

Wozu braucht man innere Klassen?

  • Logische Verbindung: Wenn eine Klasse nur für die Arbeit mit einer anderen Klasse benötigt wird und außerhalb davon keinen Sinn ergibt.
  • Verkapselung: Ermöglicht es, Implementierungsdetails zu verbergen, ohne den Paket-Namensraum zu überfrachten.
  • Zugriff auf private Mitglieder: Eine innere Klasse kann auf private Felder und Methoden der äußeren Klasse zugreifen.
  • Kompaktheit: Reduziert die Anzahl „überflüssiger“ Klassen auf Paketeebene.

Syntax der Deklaration einer inneren Klasse

Eine innere Klasse zu deklarieren ist sehr einfach: Sie wird innerhalb des Rumpfs einer anderen Klasse ohne den Modifizierer static deklariert.

class Outer {
    // Felder und Methoden der äußeren Klasse

    class Inner {
        // Felder und Methoden der inneren Klasse
        void printHello() {
            System.out.println("Hello from Inner!");
        }
    }
}

Hier ist Inner eine innere Klasse in Bezug auf Outer.

Abbildung: Visualisierung

Outer
│
├─ Felder/Methoden
│
└─ Inner (innere Klasse)
      └─ eigene Felder/Methoden

2. Wie man eine Instanz einer inneren Klasse erstellt

Hier verbirgt sich eine der häufigsten Fallen für Einsteiger! Eine Instanz einer inneren Klasse ist immer mit einem konkreten Objekt der äußeren Klasse verknüpft.

Beispiel:

Outer outer = new Outer();             // wir erzeugen ein Objekt der äußeren Klasse
Outer.Inner inner = outer.new Inner(); // wir erzeugen die innere Klasse über das äußere Objekt!
inner.printHello();                    // Hello from Inner!

Der Versuch, einfach new Inner() zu schreiben, führt zu einem Kompilierfehler, weil Java nicht weiß, mit welchem Objekt der äußeren Klasse diese innere Klasse verknüpft werden soll.

Warum ist das so?

Die innere Klasse kann auf Felder und Methoden der äußeren Klasse zugreifen. Daher muss sie „wissen“, mit welchem konkreten Objekt der äußeren Klasse sie arbeitet.

3. Beispiel für die Verwendung einer inneren Klasse

Betrachten wir ein Beispiel, in dem eine innere Klasse wirklich nützlich ist.

Beispiel 1: Modell „Rucksack und Gegenstände“

Angenommen, es gibt eine Klasse Backpack, die Gegenstände (Item) aufbewahren kann. Wir möchten jedoch, dass die Klasse Item nur innerhalb von Backpack verfügbar ist, weil uns Gegenstände außerhalb des Rucksacks nicht interessieren.

public class Backpack {
    private String owner;

    public Backpack(String owner) {
        this.owner = owner;
    }

    // Innere Klasse – ein Gegenstand kann nur im Rucksack existieren!
    class Item {
        private String name;

        public Item(String name) {
            this.name = name;
        }

        public void printInfo() {
            // Magie! Wir sehen das private Feld der äußeren Klasse
            System.out.println(owner + " hat einen Gegenstand: " + name);
        }
    }
}

Verwendung:

Backpack bp = new Backpack("Rutger");
Backpack.Item item = bp.new Item("Java-Lehrbuch");
item.printInfo(); // Rutger hat einen Gegenstand: Java-Lehrbuch

Beachten Sie: Item kann auf das Feld owner zugreifen, selbst wenn es private ist!

Beispiel 2: Iterator für eine eigene Sammlung

In Java implementieren Sammlungen oft eine innere Iterator-Klasse. Versuchen wir, eine eigene einfache Sammlung mit einer inneren Iterator-Klasse zu erstellen.

public class IntList {
    private int[] data = new int[10];
    private int size = 0;

    public void add(int value) {
        data[size++] = value;
    }

    // Innere Klasse – Iterator!
    class Iterator {
        private int index = 0;

        public boolean hasNext() {
            return index < size;
        }

        public int next() {
            return data[index++];
        }
    }
}

Verwendung:

IntList list = new IntList();
list.add(10);
list.add(20);

IntList.Iterator it = list.new Iterator();
while (it.hasNext()) {
    System.out.println(it.next());
}

4. Besonderheiten innerer Klassen

Zugriff auf Mitglieder der äußeren Klasse

Eine innere Klasse kann auf alle (sogar private) Felder und Methoden der äußeren Klasse zugreifen. Das ist praktisch, erfordert aber Vorsicht: Wenn sich die äußere Klasse ändert, kann die innere plötzlich „kaputtgehen“.

class Outer {
    private int secret = 42;

    class Inner {
        void showSecret() {
            System.out.println("Geheimnis: " + secret);
        }
    }
}

Eine innere Klasse darf keine statischen Mitglieder enthalten

Eine innere (non-static inner) Klasse darf keine statischen Felder oder Methoden enthalten, außer static final-Konstanten (zum Beispiel: public static final int MY_CONST = 123;). Das liegt daran, dass eine innere Klasse immer an eine Instanz der äußeren Klasse „gebunden“ ist.

Wenn Sie eine statisch geschachtelte Klasse deklarieren müssen – verwenden Sie den Modifizierer static (mehr dazu in der nächsten Vorlesung).

Sichtbarkeit der inneren Klasse

Eine innere Klasse kann private, protected, public sein oder Sichtbarkeit auf Paketebene besitzen (package-private).

public class Outer {
    private class Inner { /* ... */ }
}

5. Nützliche Feinheiten

Wann man innere Klassen verwenden sollte

Innere Klassen dienen nicht der Schönheit, sondern der logischen Organisation des Codes. Verwenden Sie sie, wenn:

  • Die Klasse nur an einer Stelle benötigt wird (z. B. ein Hilfs-Iterator, Handler, Teil einer komplexen Struktur).
  • Die Klasse eng mit der äußeren Klasse verknüpft ist und außerhalb davon keinen Sinn ergibt.
  • Sie Implementierungsdetails vor anderen Klassen im Paket verbergen möchten.

Verwenden Sie innere Klassen nicht nur der Mode halber! Manchmal ist es besser, die Klasse auf die oberste Ebene auszulagern, wenn sie auch an anderen Stellen nützlich sein kann.

Innere Klasse und this

Innerhalb einer inneren Klasse kann man mit OuterClassName.this auf Felder und Methoden der äußeren Klasse zugreifen.

class Car {
    private String model = "Tesla";

    class Engine {
        void printModel() {
            // Expliziter Zugriff auf das äußere Objekt
            System.out.println("Modell: " + Car.this.model);
        }
    }
}

Diese Syntax ist besonders nützlich, wenn innere und äußere Klasse Felder mit denselben Namen haben.

Wann man KEINE inneren Klassen verwenden sollte

Verwenden Sie sie nicht, wenn:

  • Die innere Klasse nicht auf Felder/Methoden der äußeren Klasse zugreift
  • Die Klasse an anderen Stellen des Programms nützlich sein könnte
  • Die äußere Klasse zu groß und zu komplex wird

In solchen Fällen ist es besser:

  • Die Klasse als statisch geschachtelte Klasse zu deklarieren (static class)
  • Die Klasse in eine separate Datei auszulagern
  • Statt einer ganzen Klasse normale Methoden zu verwenden

6. Typische Fehler beim Arbeiten mit inneren Klassen

Fehler Nr. 1: Versuch, eine innere Klasse ohne Objekt der äußeren Klasse zu erstellen.
Wenn man new Inner() schreibt, meldet der Compiler einen Fehler: "No enclosing instance of type Outer is accessible". Erstellen Sie eine innere Klasse immer über ein Objekt der äußeren Klasse: outer.new Inner().

Fehler Nr. 2: Versuch, statische Felder oder Methoden in einer inneren Klasse zu deklarieren.
Eine innere Klasse darf keine statischen Mitglieder enthalten (außer Konstanten). Falls nötig – verwenden Sie eine statisch geschachtelte Klasse, also deklarieren Sie die Klasse mit dem Modifizierer static.

Fehler Nr. 3: Übermäßiger Einsatz innerer Klassen.
Wenn eine innere Klasse nicht auf Felder/Methoden der äußeren Klasse zugreift, sollte sie vermutlich static sein oder ganz ausgelagert werden. Erstellen Sie keine inneren Klassen „für alle Fälle“.

Fehler Nr. 4: Verwechslung beim Zugriff auf Felder der äußeren Klasse.
Wenn innere und äußere Klasse Felder mit denselben Namen haben, verwenden Sie OuterClassName.this.field, um explizit auf das Feld der äußeren Klasse zuzugreifen.

1
Aufgabe
JAVA 25 SELF, Level 16, Lektion 0
Gesperrt
Lagerverwaltung: Kennzeichnung von Kartons 📦
Lagerverwaltung: Kennzeichnung von Kartons 📦
1
Aufgabe
JAVA 25 SELF, Level 16, Lektion 0
Gesperrt
Persönlicher Assistent: höfliche Begrüßung 🗣️
Persönlicher Assistent: höfliche Begrüßung 🗣️
1
Aufgabe
JAVA 25 SELF, Level 16, Lektion 0
Gesperrt
Magische Bibliothek: Katalogisierung von Schriftrollen 📜
Magische Bibliothek: Katalogisierung von Schriftrollen 📜
1
Aufgabe
JAVA 25 SELF, Level 16, Lektion 0
Gesperrt
Smart Home: Adressierung von Räumen 🏠
Smart Home: Adressierung von Räumen 🏠
Kommentare
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION