2071 Stimmen

Java innere Klasse und statische verschachtelte Klasse

Was ist der Hauptunterschied zwischen einer inneren Klasse und einer statischen verschachtelten Klasse in Java? Spielt der Entwurf/die Implementierung eine Rolle bei der Wahl einer dieser beiden Klassen?

99 Stimmen

Die Antwort von Joshua Bloch ist in Leistungsfähiges Java lesen item 22 : Favor static member classes over non static

24 Stimmen

Für das Protokoll, es ist Punkt 24 in der 3. Auflage des gleichen Buches.

1903voto

Martin Punkte 20765

Von der Java-Tutorial :

Verschachtelte Klassen werden in zwei Kategorien unterteilt: statische und nicht-statische. Verschachtelte Klassen, die als statisch deklariert sind, werden einfach statische verschachtelte Klassen genannt. Nicht statische verschachtelte Klassen werden als innere Klassen bezeichnet.

Auf statische verschachtelte Klassen wird über den Namen der sie umschließenden Klasse zugegriffen:

OuterClass.StaticNestedClass

Um zum Beispiel ein Objekt für die statische verschachtelte Klasse zu erstellen, verwenden Sie diese Syntax:

OuterClass.StaticNestedClass nestedObject = new OuterClass.StaticNestedClass();

Objekte, die Instanzen einer inneren Klasse sind, existieren innerhalb einer Instanz der äußeren Klasse. Betrachten Sie die folgenden Klassen:

class OuterClass {
    ...
    class InnerClass {
        ...
    }
}

Eine Instanz von InnerClass kann nur innerhalb einer Instanz von OuterClass existieren und hat direkten Zugriff auf die Methoden und Felder der sie umschließenden Instanz.

Um eine innere Klasse zu instanziieren, müssen Sie zunächst die äußere Klasse instanziieren. Dann erstellen Sie das innere Objekt innerhalb des äußeren Objekts mit dieser Syntax:

OuterClass outerObject = new OuterClass()
OuterClass.InnerClass innerObject = outerObject.new InnerClass();

siehe: Java Tutorial - Verschachtelte Klassen

Der Vollständigkeit halber sei darauf hingewiesen, dass es auch so etwas wie eine innere Klasse ohne eine umschließende Instanz :

class A {
  int t() { return 1; }
  static A a =  new A() { int t() { return 2; } };
}

Hier, new A() { ... } ist ein innere Klasse, die in einem statischen Kontext definiert ist und hat keine umschließende Instanz.

673voto

Jegschemesch Punkte 11134

El Java-Tutorial sagt :

Terminologie: Verschachtelte Klassen werden in zwei Kategorien unterteilt: statisch und nicht-statische. Verschachtelte Klassen, die statisch deklariert sind, werden einfach als statische verschachtelte Klassen. Nicht-statische verschachtelte Klassen werden als innere Klassen bezeichnet.

Im allgemeinen Sprachgebrauch werden die Begriffe "verschachtelt" und "inner" von den meisten Programmierern synonym verwendet, aber ich werde den korrekten Begriff "verschachtelte Klasse" verwenden, der sowohl innere als auch statische Klassen umfasst.

Klassen können verschachtelt werden ad infinitum So kann z. B. die Klasse A die Klasse B enthalten, die wiederum die Klasse C enthält, die wiederum die Klasse D enthält, usw. Eine Verschachtelung von Klassen auf mehr als einer Ebene ist jedoch selten, da sie im Allgemeinen schlecht konzipiert ist.

Es gibt drei Gründe, warum Sie eine verschachtelte Klasse erstellen können:

  • Organisation: Manchmal erscheint es sinnvoll, eine Klasse in den Namensraum einer anderen Klasse einzusortieren, insbesondere wenn sie in keinem anderen Kontext verwendet wird.
  • Zugriff: verschachtelte Klassen haben besonderen Zugriff auf die Variablen/Felder der sie enthaltenden Klassen (welche Variablen/Felder genau, hängt von der Art der verschachtelten Klasse ab, ob inner oder statisch).
  • Bequemlichkeit: Es ist lästig, für jeden neuen Typ eine neue Datei zu erstellen, vor allem, wenn der Typ nur in einem einzigen Kontext verwendet wird.

Es gibt vier Arten von verschachtelten Klassen in Java . Kurz gesagt, sind sie:

  • statische Klasse : deklariert als statisches Mitglied einer anderen Klasse
  • innere Klasse : deklariert als Instanzmitglied einer anderen Klasse
  • lokale innere Klasse : deklariert innerhalb einer Instanzmethode einer anderen Klasse
  • anonyme innere Klasse : wie eine lokale innere Klasse, aber als Ausdruck geschrieben, der ein einmaliges Objekt zurückgibt

Lassen Sie mich das näher erläutern.

Statische Klassen

Statische Klassen sind am einfachsten zu verstehen, da sie nichts mit Instanzen der enthaltenen Klasse zu tun haben.

Eine statische Klasse ist eine Klasse, die als statisches Mitglied einer anderen Klasse deklariert ist. Genau wie andere statische Mitglieder ist eine solche Klasse eigentlich nur ein Anhängsel, das die enthaltende Klasse als Namensraum verwendet, z.B.. die Klasse Ziege deklariert als statisches Mitglied der Klasse Nashorn im Paket pizza ist bekannt unter dem Namen pizza.Nashorn.Ziege .

package pizza;

public class Rhino {

    ...

    public static class Goat {
        ...
    }
}

Offen gesagt sind statische Klassen eine ziemlich wertlose Funktion, da Klassen bereits durch Pakete in Namensräume unterteilt sind. Der einzige wirklich denkbare Grund, eine statische Klasse zu erstellen, besteht darin, dass eine solche Klasse Zugriff auf die privaten statischen Mitglieder der Klasse hat, die sie enthält, aber ich halte das für eine ziemlich schwache Rechtfertigung für die Existenz der statischen Klassenfunktion.

Innere Klassen

Eine innere Klasse ist eine Klasse, die als nicht-statisches Mitglied einer anderen Klasse deklariert ist:

package pizza;

public class Rhino {

    public class Goat {
        ...
    }

    private void jerry() {
        Goat g = new Goat();
    }
}

Wie bei einer statischen Klasse wird die innere Klasse durch den Namen der enthaltenen Klasse qualifiziert, pizza.Nashorn.Ziege aber innerhalb der Klasse, die sie enthält, kann sie unter ihrem einfachen Namen bekannt sein. Jede Instanz einer inneren Klasse ist jedoch an eine bestimmte Instanz der sie enthaltenden Klasse gebunden: oben ist die Ziege erstellt in jerry ist implizit mit dem Nashorn Instanz este en jerry . Andernfalls machen wir die zugehörige Nashorn Instanz explizit, wenn wir instanziieren Ziege :

Rhino rhino = new Rhino();
Rhino.Goat goat = rhino.new Goat();

(Beachten Sie, dass Sie sich auf den inneren Typ nur als Ziege im Seltsamen neu Syntax: Java schlussfolgert den enthaltenen Typ aus der Nashorn Teil. Und, ja new rhino.Ziege() hätte für mich auch mehr Sinn ergeben).

Was bringt uns das also? Nun, die innere Klasseninstanz hat Zugang zu den Instanzmitgliedern der umgebenden Klasseninstanz. Auf diese umschließenden Instanzmitglieder wird innerhalb der inneren Klasse verwiesen über nur ihre einfachen Namen, nicht über este ( este in der inneren Klasse bezieht sich auf die innere Klasseninstanz, nicht auf die zugehörige enthaltende Klasseninstanz):

public class Rhino {

    private String barry;

    public class Goat {
        public void colin() {
            System.out.println(barry);
        }
    }
}

In der inneren Klasse können Sie sich auf este der enthaltenen Klasse als Nashorn.dies , und Sie können este auf ihre Mitglieder zu beziehen, z.B. Rhino.this.barry .

Lokale innere Klassen

Eine lokale innere Klasse ist eine Klasse, die im Körper einer Methode deklariert wird. Eine solche Klasse ist nur innerhalb der sie enthaltenden Methode bekannt, so dass sie nur innerhalb der sie enthaltenden Methode instanziiert werden kann und auf ihre Mitglieder zugegriffen werden kann. Der Vorteil ist, dass eine lokale innere Klasseninstanz an die finalen lokalen Variablen der enthaltenden Methode gebunden ist und auf diese zugreifen kann. Wenn die Instanz eine finale lokale Variable ihrer enthaltenden Methode verwendet, behält die Variable den Wert, den sie zum Zeitpunkt der Erstellung der Instanz hatte, selbst wenn die Variable den Gültigkeitsbereich verlassen hat (dies ist effektiv Javas grobe, begrenzte Version von Closures).

Da eine lokale innere Klasse weder Mitglied einer Klasse noch eines Pakets ist, wird sie nicht mit einer Zugriffsebene deklariert. (Es muss jedoch klar sein, dass ihre eigenen Mitglieder Zugriffsrechte wie in einer normalen Klasse haben).

Wenn eine lokale innere Klasse in einer Instanzmethode deklariert wird, ist eine Instanziierung der inneren Klasse an die Instanz gebunden, die von der enthaltenen Methode este zum Zeitpunkt der Erstellung der Instanz, und so sind die Instanzmitglieder der enthaltenden Klasse wie in einer inneren Instanzklasse zugänglich. Eine lokale innere Klasse wird einfach instanziiert über seinen Namen, z.B.. lokale innere Klasse Katze wird instanziiert als neue Katze() und nicht new this.Cat(), wie Sie vielleicht erwarten.

Anonyme innere Klassen

Eine anonyme innere Klasse ist eine syntaktisch bequeme Möglichkeit, eine lokale innere Klasse zu schreiben. In der Regel wird eine lokale innere Klasse höchstens einmal instanziiert, wenn die sie enthaltende Methode ausgeführt wird. Es wäre also schön, wenn wir die Definition der lokalen inneren Klasse und ihre einmalige Instanziierung in einer bequemen Syntaxform zusammenfassen könnten, und es wäre auch schön, wenn wir uns keinen Namen für die Klasse ausdenken müssten (je weniger wenig hilfreiche Namen Ihr Code enthält, desto besser). Eine anonyme innere Klasse ermöglicht diese beiden Dinge:

new *ParentClassName*(*constructorArgs*) {*members*}

Dies ist ein Ausdruck, der eine neue Instanz einer unbenannten Klasse zurückgibt, die die ParentClassName . Sie können keinen eigenen Konstruktor angeben; stattdessen wird implizit ein Konstruktor angegeben, der einfach den Superkonstruktor aufruft, so dass die angegebenen Argumente zum Superkonstruktor passen müssen. (Wenn der übergeordnete Konstruktor mehrere Konstruktoren enthält, wird der "einfachste" Konstruktor aufgerufen, der durch eine ziemlich komplexe Reihe von Regeln bestimmt wird, die es nicht wert sind, im Detail gelernt zu werden - achten Sie einfach darauf, was NetBeans oder Eclipse Ihnen sagen).

Alternativ können Sie auch eine Schnittstelle angeben, die Sie implementieren möchten:

new *InterfaceName*() {*members*}

Eine solche Deklaration erzeugt eine neue Instanz einer unbenannten Klasse, die Object erweitert und Folgendes implementiert InterfaceName . Auch hier können Sie keinen eigenen Konstruktor bereitstellen; in diesem Fall stellt Java implizit einen Konstruktor ohne Argumente zur Verfügung (daher gibt es in diesem Fall niemals Konstruktorargumente).

Auch wenn Sie einer anonymen inneren Klasse keinen Konstruktor geben können, können Sie mit einem Initialisierungsblock (einem {}-Block, der außerhalb einer Methode platziert wird) alle gewünschten Einstellungen vornehmen.

Seien Sie sich darüber im Klaren, dass eine anonyme innere Klasse einfach eine weniger flexible Art ist, eine lokale innere Klasse mit einer Instanz zu erstellen. Wenn Sie eine lokale innere Klasse wollen, die mehrere Schnittstellen implementiert oder die Schnittstellen implementiert, während sie eine andere Klasse als Objekt oder die ihren eigenen Konstruktor angibt, müssen Sie eine normale benannte lokale innere Klasse erstellen.

170voto

jrudolph Punkte 8137

Ich glaube nicht, dass der wirkliche Unterschied in den obigen Antworten deutlich geworden ist.

Zuerst müssen die Begriffe richtig verstanden werden:

  • Eine verschachtelte Klasse ist eine Klasse, die in einer anderen Klasse auf Quellcodeebene enthalten ist.
  • Er ist statisch, wenn Sie ihn mit der Option statisch Modifikator.
  • Eine nicht-statische verschachtelte Klasse wird als innere Klasse bezeichnet. (Ich bleibe bei der nicht-statischen verschachtelten Klasse.)

Die Antwort von Martin ist so weit richtig. Die eigentliche Frage ist jedoch: Welchen Zweck hat es, eine verschachtelte Klasse als statisch zu deklarieren oder nicht?

Sie verwenden statische verschachtelte Klassen wenn Sie Ihre Klassen nur dann zusammenhalten wollen, wenn sie thematisch zusammengehören oder wenn die verschachtelte Klasse ausschließlich in der umschließenden Klasse verwendet wird. Es gibt keinen semantischen Unterschied zwischen einer statischen verschachtelten Klasse und jeder anderen Klasse.

Nicht-statische verschachtelte Klassen sind eine andere Sache. Ähnlich wie anonyme innere Klassen sind solche verschachtelten Klassen eigentlich Schließungen. Das heißt, sie erfassen den sie umgebenden Bereich und die sie umschließende Instanz und machen sie zugänglich. Vielleicht kann ein Beispiel dies verdeutlichen. Siehe diesen Stub eines Containers:

public class Container {
    public class Item{
        Object data;
        public Container getContainer(){
            return Container.this;
        }
        public Item(Object data) {
            super();
            this.data = data;
        }

    }

    public static Item create(Object data){
        // does not compile since no instance of Container is available
        return new Item(data);
    }
    public Item createSubItem(Object data){
        // compiles, since 'this' Container is available
        return new Item(data);
    }
}

In diesem Fall möchten Sie einen Verweis von einem untergeordneten Element auf den übergeordneten Container haben. Mit einer nicht statischen verschachtelten Klasse funktioniert dies ohne großen Aufwand. Sie können auf die umschließende Instanz von Container mit der Syntax Container.this .

Weitere Erklärungen zum Thema folgen:

Wenn Sie sich den Java-Bytecode ansehen, den der Compiler für eine (nicht statische) verschachtelte Klasse erzeugt, wird es vielleicht noch deutlicher:

// class version 49.0 (49)
// access flags 33
public class Container$Item {

  // compiled from: Container.java
  // access flags 1
  public INNERCLASS Container$Item Container Item

  // access flags 0
  Object data

  // access flags 4112
  final Container this$0

  // access flags 1
  public getContainer() : Container
   L0
    LINENUMBER 7 L0
    ALOAD 0: this
    GETFIELD Container$Item.this$0 : Container
    ARETURN
   L1
    LOCALVARIABLE this Container$Item L0 L1 0
    MAXSTACK = 1
    MAXLOCALS = 1

  // access flags 1
  public <init>(Container,Object) : void
   L0
    LINENUMBER 12 L0
    ALOAD 0: this
    ALOAD 1
    PUTFIELD Container$Item.this$0 : Container
   L1
    LINENUMBER 10 L1
    ALOAD 0: this
    INVOKESPECIAL Object.<init>() : void
   L2
    LINENUMBER 11 L2
    ALOAD 0: this
    ALOAD 2: data
    PUTFIELD Container$Item.data : Object
    RETURN
   L3
    LOCALVARIABLE this Container$Item L0 L3 0
    LOCALVARIABLE data Object L0 L3 2
    MAXSTACK = 2
    MAXLOCALS = 3
}

Wie Sie sehen können, erstellt der Compiler ein verstecktes Feld Container this$0 . Dies wird im Konstruktor festgelegt, der einen zusätzlichen Parameter vom Typ Container hat, um die umschließende Instanz anzugeben. Im Quelltext ist dieser Parameter nicht zu sehen, aber der Compiler erzeugt ihn implizit für eine verschachtelte Klasse.

Martins Beispiel

OuterClass.InnerClass innerObject = outerObject.new InnerClass();

würde so zu einem Aufruf von etwas wie (in Bytecodes) kompiliert werden

new InnerClass(outerObject)

Der Vollständigkeit halber:

Eine anonyme Klasse est ist ein perfektes Beispiel für eine nicht statische, verschachtelte Klasse, der kein Name zugeordnet ist und auf die später nicht verwiesen werden kann.

107voto

aleroot Punkte 68601

Ich denke, dass keine der obigen Antworten Ihnen den wirklichen Unterschied zwischen einer verschachtelten Klasse und einer statischen verschachtelten Klasse in Bezug auf das Anwendungsdesign erklärt:

Übersicht

Eine verschachtelte Klasse kann nichtstatisch oder statisch sein und in jedem Fall ist eine Klasse, die innerhalb einer anderen Klasse definiert ist . Eine verschachtelte Klasse sollte nur dazu da sein, der sie umgebenden Klasse zu dienen. Wenn eine verschachtelte Klasse von anderen Klassen (nicht nur der einschließenden) genutzt wird, sollte sie als Klasse der obersten Ebene deklariert werden.

Unterschied

Nichtstatische verschachtelte Klasse : implizit mit der umschließenden Instanz der enthaltenden Klasse assoziiert ist, bedeutet dies, dass es möglich ist, Methoden aufzurufen und auf Variablen der umschließenden Instanz zuzugreifen. Eine häufige Verwendung einer nicht-statischen verschachtelten Klasse ist die Definition einer Adapter-Klasse.

Statische verschachtelte Klasse statische verschachtelte Klasse: kann nicht auf die Instanz der umschließenden Klasse zugreifen und Methoden auf ihr aufrufen, sollte also verwendet werden, wenn die verschachtelte Klasse keinen Zugriff auf eine Instanz der umschließenden Klasse benötigt. Eine häufige Verwendung der statischen verschachtelten Klasse ist die Implementierung einer Komponente des äußeren Objekts.

Schlussfolgerung

Der Hauptunterschied zwischen den beiden ist also vom Standpunkt der Gestaltung aus gesehen: nichtstatische verschachtelte Klasse kann auf die Instanz der Containerklasse zugreifen, während statische Klassen dies nicht können .

40voto

Behzad Bahmanyar Punkte 5936

Hier sind die wichtigsten Unterschiede und Gemeinsamkeiten zwischen Java inner class und static nested class.

Hoffentlich hilft das!

Innere Klasse

  • Kann auf zur äußeren Klasse sowohl Instanz als auch statisch Methoden und Felder

  • Mit der Instanz der umschließenden Klasse assoziiert um sie zu instanziieren, wird also zunächst eine Instanz der äußeren Klasse benötigt (beachten Sie neu Stichwort Platz):

    Outerclass.InnerClass innerObject = outerObject.new Innerclass();
  • Kann nicht beliebig definieren statische Mitglieder selbst

  • Kann nicht haben Klasse o Schnittstelle Erklärung

Statische verschachtelte Klasse

  • Kein Zugriff möglich äußere Klasse Instanz Methoden oder Felder

  • Nicht mit einer Instanz der umschließenden Klasse verbunden Um es also zu instanziieren:

    OuterClass.StaticNestedClass nestedObject = new OuterClass.StaticNestedClass();

Ähnlichkeiten

  • Beide Innere Klassen kann sogar auf private Felder und Methoden von äußere Klasse
  • Auch die Äußere Klasse Zugang haben zu private Felder und Methoden von innere Klassen
  • Beide Klassen können private, geschützte oder öffentliche Zugriffsmodifikatoren haben

Warum verschachtelte Klassen verwenden?

Laut Oracle-Dokumentation gibt es dafür mehrere Gründe ( vollständige Dokumentation ):

  • Auf diese Weise lassen sich Klassen, die nur an einem Ort verwendet werden, logisch gruppieren: Wenn eine Klasse nur für eine andere Klasse nützlich ist, dann ist es logisch, sie in diese Klasse einzubetten und die beiden zusammenzuhalten. Die Verschachtelung solcher "Hilfsklassen" macht ihr Paket schlanker.

  • Sie erhöht die Verkapselung: Nehmen wir an, es gibt zwei Top-Level-Klassen, A und B, und B benötigt Zugriff auf Mitglieder von A, die andernfalls als privat deklariert werden würden. Indem die Klasse B innerhalb der Klasse A versteckt wird, können die Mitglieder von A als privat deklariert werden, und B kann auf sie zugreifen. Darüber hinaus kann B selbst vor der Außenwelt verborgen werden.

  • Dies kann zu besser lesbarem und wartbarem Code führen: Durch die Verschachtelung kleinerer Klassen innerhalb von Top-Level-Klassen wird der Code näher an den Ort der Verwendung gebracht.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X