Statisch verschachtelte Klasse in Java, warum?

217

Ich habe mir den Java-Code angesehen LinkedListund festgestellt, dass er eine statisch verschachtelte Klasse verwendet Entry.

public class LinkedList<E> ... {
...

 private static class Entry<E> { ... }

}

Was ist der Grund für die Verwendung einer statisch verschachtelten Klasse anstelle einer normalen inneren Klasse?

Der einzige Grund, an den ich denken konnte, war, dass Entry keinen Zugriff auf Instanzvariablen hat und daher aus OOP-Sicht eine bessere Kapselung aufweist.

Aber ich dachte, es könnte andere Gründe geben, vielleicht Leistung. Was könnte es sein?

Hinweis. Ich hoffe, ich habe meine Begriffe richtig verstanden, ich hätte es eine statische innere Klasse genannt, aber ich denke, das ist falsch: http://java.sun.com/docs/books/tutorial/java/javaOO/nested.html

David Turner
quelle

Antworten:

271

Die Sun-Seite, auf die Sie verlinken, weist einige wesentliche Unterschiede auf:

Eine verschachtelte Klasse ist Mitglied ihrer umschließenden Klasse. Nicht statische verschachtelte Klassen (innere Klassen) haben Zugriff auf andere Mitglieder der einschließenden Klasse, auch wenn sie als privat deklariert sind. Statisch verschachtelte Klassen haben keinen Zugriff auf andere Mitglieder der einschließenden Klasse.
...

Hinweis: Eine statisch verschachtelte Klasse interagiert mit den Instanzmitgliedern ihrer äußeren Klasse (und anderen Klassen) wie jede andere Klasse der obersten Ebene. Tatsächlich ist eine statisch verschachtelte Klasse verhaltensmäßig eine Klasse der obersten Ebene, die aus Bequemlichkeitsgründen in einer anderen Klasse der obersten Ebene verschachtelt wurde.

Es ist nicht erforderlich LinkedList.Entry, eine Klasse der obersten Ebene zu sein, da diese nur von verwendet wird LinkedList(es gibt einige andere Schnittstellen, für die auch statische verschachtelte Klassen benannt sind Entry, z. B. Map.Entry- dasselbe Konzept). Und da es keinen Zugriff auf die Mitglieder von LinkedList benötigt, ist es sinnvoll, statisch zu sein - es ist ein viel saubererer Ansatz.

Wie Jon Skeet betont, ist es meiner Meinung nach eine bessere Idee, wenn Sie eine verschachtelte Klasse verwenden, zunächst statisch zu sein und dann anhand Ihrer Verwendung zu entscheiden, ob sie wirklich nicht statisch sein muss.

matt b
quelle
Bah, ich kann anscheinend keinen #comment113712_253507
Ankerlink
1
@matt b Wenn eine statisch verschachtelte Klasse keinen Zugriff auf die Instanzmitglieder der Outer-Klasse hat, wie interagiert sie mit den Instanzmitgliedern der Outer-Klasse?
Geek
1
@mattb Aber wie @Geek bemerkt hat, ist die Sun-Seite widersprüchlich: A static nested class interacts with the instance members of its outer class (and other classes) just like any other top-level class Wie ist das möglich, wenn nur ein Absatz vor den Dokumenten Static nested classes do not have access to other members of the enclosing classA nested (non-static) class interacts with the instance members of its outer class (and other classes) just like any other top-level class
Folgendes
1
@ DavidS Danke für den Link! Ja, ich habe mich geirrt und beim Lesen meines Kommentars sehe ich, dass meine Umformulierung falsch war. Wie Sie gesagt hat : haben An inner class interacts with the instance members through an implicit reference to its enclosing classund dass dies aus einer anderen interessanten Eigenschaft non-static inner classessowie anonymous inner classesoder local classes defined inside a block: sie alle nicht haben können eine no-argKonstruktor Ursache der Compiler wird implizit die ARG - Sequenz eines jeden Konstruktor , um prepend eine Referenz einer Instanz des einschließe passieren Klasse. Ziemlich einfach.
Tonix
1
Sie können die statische innere Klasse verwenden, um die äußere Klasse zu instanziieren, die nur einen privaten Konstruktor hat. Dies wird im Builder-Muster verwendet. Mit der inneren Klasse kann man nicht dasselbe machen.
Seenimurugan
47

Meiner Meinung nach sollte die Frage umgekehrt sein, wenn Sie eine innere Klasse sehen - tut es wirklich eine innere Klasse sein, mit der zusätzlichen Komplexität und dem impliziten (und nicht expliziten und klareren IMO) Verweis auf eine Instanz der enthaltenden Klasse?

Wohlgemerkt, ich bin als C # -Fan voreingenommen - C # hat nicht das Äquivalent zu inneren Klassen, obwohl es verschachtelte Typen hat. Ich kann nicht sagen, dass ich den inneren Unterricht noch verpasst habe :)

Jon Skeet
quelle
4
Ich könnte mich irren, aber das sieht für mich wie ein Beispiel für eine statisch verschachtelte Klasse aus, nicht für eine innere Klasse. Sie geben im Beispiel sogar an, dass sie keinen Zugriff auf Instanzvariablen der umgebenden Klasse in der verschachtelten Klasse haben.
ColinD
Ja, Colin hat recht - C # hat keine inneren Klassen, es hat verschachtelte Klassen. Wohlgemerkt, eine statisch verschachtelte Klasse in C # ist nicht dasselbe wie eine statisch verschachtelte Klasse in Java :)
Jon Skeet
2
Verschachtelte Typen sind einer der Bereiche, in denen C # im Vergleich zu Java äußerst korrekt ist. Ich staune immer über seine semantische / logische Korrektheit.
nawfal
3
@nawfal: Ja, abgesehen von ein paar Kleinigkeiten bin ich beeindruckt, wie gut die C # -Sprache entworfen (und spezifiziert) wurde.
Jon Skeet
1
@ JonSkeet Hast du einen Artikel oder ein Blog darüber, was diese Kleinigkeiten sind? Ich würde gerne durch das gehen, was du als "Niggles"
findest
27

Hier sind nicht offensichtliche Probleme mit der Speicheraufbewahrung zu berücksichtigen. Da eine nicht statische innere Klasse einen impliziten Verweis auf ihre 'äußere' Klasse beibehält, wird auch auf die äußere Instanz stark verwiesen, wenn auf eine Instanz der inneren Klasse stark verwiesen wird. Dies kann zu Kopfkratzern führen, wenn die äußere Klasse nicht durch Müll gesammelt wird, obwohl anscheinend nichts darauf verweist.

Leigh
quelle
Wenn die 'äußere' Klasse endgültig ist und daher überhaupt nicht instanziiert werden kann, ist dieses Argument in diesem Fall sinnvoll? Weil es nutzlos ist, einen Verweis auf eine äußere Klasse zu haben / zu behalten, wenn diese endgültig ist.
Getsadzeg
10

Zum einen haben nicht statische innere Klassen ein zusätzliches, verstecktes Feld, das auf die Instanz der äußeren Klasse verweist. Wenn die Entry-Klasse also nicht statisch wäre, hätte sie nicht nur Zugriff, den sie nicht benötigt, sondern auch vier statt drei Zeiger.

In der Regel würde ich sagen, wenn Sie eine Klasse definieren, die im Grunde genommen als Sammlung von Datenelementen fungiert, wie z. B. eine "Struktur" in C, sollten Sie in Betracht ziehen, sie statisch zu machen.


quelle
8

Statische innere Klasse wird im Builder-Muster verwendet. Die statische innere Klasse kann ihre äußere Klasse instanziieren, die nur einen privaten Konstruktor hat. Sie können also die statische innere Klasse verwenden, um die äußere Klasse zu instanziieren, die nur einen privaten Konstruktor hat. Sie können mit der inneren Klasse nicht dasselbe tun, da vor dem Zugriff auf die innere Klasse ein Objekt der äußeren Klasse erstellt werden muss.

class OuterClass {
    private OuterClass(int x) {
        System.out.println("x: " + x);
    }

    static class InnerClass {
        public static void test() {
            OuterClass outer = new OuterClass(1);
        }
    }
}

public class Test {
    public static void main(String[] args) {
        OuterClass.InnerClass.test();
        // OuterClass outer = new OuterClass(1); // It is not possible to create outer instance from outside.
    }
}

Dies gibt x: 1 aus

gesehenimurugan
quelle
7

Die statisch verschachtelte Klasse ist wie jede andere äußere Klasse, da sie keinen Zugriff auf Mitglieder der äußeren Klasse hat.

Nur zur Vereinfachung der Verpackung können wir statisch verschachtelte Klassen aus Gründen der Lesbarkeit in eine äußere Klasse einteilen. Abgesehen davon gibt es keinen anderen Anwendungsfall für statisch verschachtelte Klassen.

Ein Beispiel für eine solche Verwendung finden Sie in der Android R.java-Datei (Ressourcen). Der Res-Ordner von Android enthält Layouts (mit Bildschirmdesigns), einen Zeichenordner (mit für das Projekt verwendeten Bildern), einen Werteordner (mit Zeichenfolgenkonstanten) usw.

Da alle Ordner Teil des Res-Ordners sind, generiert das Android-Tool eine R.java-Datei (Ressourcen), die intern viele statisch verschachtelte Klassen für jeden ihrer inneren Ordner enthält.

Hier ist das Erscheinungsbild der in Android generierten R.java-Datei: Hier werden sie nur zur Vereinfachung der Verpackung verwendet.

/* AUTO-GENERATED FILE.  DO NOT MODIFY.
 *
 * This class was automatically generated by the
 * aapt tool from the resource data it found.  It
 * should not be modified by hand.
 */

package com.techpalle.b17_testthird;

public final class R {
    public static final class drawable {
        public static final int ic_launcher=0x7f020000;
    }
    public static final class layout {
        public static final int activity_main=0x7f030000;
    }
    public static final class menu {
        public static final int main=0x7f070000;
    }
    public static final class string {
        public static final int action_settings=0x7f050001;
        public static final int app_name=0x7f050000;
        public static final int hello_world=0x7f050002;
    }
}
user1923551
quelle
6

Von http://docs.oracle.com/javase/tutorial/java/javaOO/whentouse.html :

Verwenden Sie eine nicht statische verschachtelte Klasse (oder innere Klasse), wenn Sie Zugriff auf die nicht öffentlichen Felder und Methoden einer umschließenden Instanz benötigen. Verwenden Sie eine statisch verschachtelte Klasse, wenn Sie diesen Zugriff nicht benötigen.

John29
quelle
4

Einfaches Beispiel:

package test;

public class UpperClass {
public static class StaticInnerClass {}

public class InnerClass {}

public static void main(String[] args) {
    // works
    StaticInnerClass stat = new StaticInnerClass();
    // doesn't compile
    InnerClass inner = new InnerClass();
}
}

Wenn die Klasse nicht statisch ist, kann sie nur in einer Instanz der oberen Klasse instanziiert werden (also nicht in dem Beispiel, in dem main eine statische Funktion ist).

Vinze
quelle
Ihre StaticInnerClass ist in der Tat keine statische verschachtelte / innere Klasse. Es ist eine statische Klasse der obersten Ebene.
TheRiley
2

Einer der Gründe für statisch oder normal ist das Laden von Klassen. Sie können eine innere Klasse im Konstruktor ihres übergeordneten Elements nicht instanziieren.

PS: Ich habe immer verstanden, dass "verschachtelt" und "inner" austauschbar sind. Es mag subtile Nuancen in den Begriffen geben, aber die meisten Java-Entwickler würden es auch verstehen.

Mark Renouf
quelle
1

Nicht statische innere Klassen können zu Speicherlecks führen, während statische innere Klassen vor ihnen schützen. Wenn die äußere Klasse beträchtliche Daten enthält, kann dies die Leistung der Anwendung beeinträchtigen.

Die Waffe
quelle
'statisches Inneres' ist ein Widerspruch.
Marquis von Lorne
1
@EJP, meine Güte ... Leute steigen wirklich aus, indem sie darauf hinweisen, wann immer jemand "statische innere Klassen" erwähnt ...
Sakiboy
0

Ich weiß nichts über Leistungsunterschiede, aber wie Sie sagen, ist eine statisch verschachtelte Klasse nicht Teil einer Instanz der einschließenden Klasse. Es scheint nur einfacher zu sein, eine statisch verschachtelte Klasse zu erstellen, es sei denn, Sie benötigen sie wirklich als innere Klasse.

Es ist ein bisschen so, warum ich meine Variablen in Java immer endgültig mache - wenn sie nicht endgültig sind, weiß ich, dass mit ihnen etwas Lustiges los ist. Wenn Sie eine innere Klasse anstelle einer statisch verschachtelten Klasse verwenden, sollte es einen guten Grund geben.


quelle
Eine innner Klasse ist auch nicht 'Teil einer Instanz der einschließenden Klasse'.
Marquis von Lorne
Eine innere Klasse ist existenziell von der einschließenden Klasse abhängig und hat engen Zugriff auf die Mitglieder der einschließenden Klasse, sodass sie tatsächlich Teil der einschließenden Klasse ist. in der Tat ist es ein Mitglied.
TheRiley
0

Die Verwendung einer statischen verschachtelten Klasse anstelle einer nicht statischen Klasse kann in einigen Fällen Leerzeichen sparen. Zum Beispiel: Implementieren eines Comparatorinnerhalb einer Klasse, sagen wir Student.

public class Student {
  public static final Comparator<Student> BY_NAME = new ByName();
  private final String name;
  ...
  private static class ByName implements Comparator<Student> {
    public int compare() {...}
  }
}

Dann staticstellt das sicher, dass die Schülerklasse nur einen Komparator hat, anstatt jedes Mal, wenn eine neue Schülerinstanz erstellt wird, einen neuen zu instanziieren.

JenkinsY
quelle
-1

Vorteil der inneren Klasse--

  1. Einmalgebrauch
  2. unterstützt und verbessert die Kapselung
  3. Lesbarkeit
  4. privater Feldzugang

Ohne die Existenz der äußeren Klasse wird die innere Klasse nicht existieren.

class car{
    class wheel{

    }
}

Es gibt vier Arten von innerer Klasse.

  1. normale innere Klasse
  2. Methode Lokale innere Klasse
  3. Anonyme innere Klasse
  4. statische innere Klasse

Punkt ---

  1. Von der statischen inneren Klasse aus können wir nur auf statische Mitglieder der äußeren Klasse zugreifen.
  2. Innerhalb der inneren Klasse können wir kein statisches Mitglied deklarieren.
  3. um die normale innere Klasse im statischen Bereich der äußeren Klasse aufzurufen.

    Outer 0=new Outer(); Outer.Inner i= O.new Inner();

  4. um die normale innere Klasse im Instanzbereich der äußeren Klasse aufzurufen.

    Inner i=new Inner();

  5. um eine normale innere Klasse außerhalb der äußeren Klasse aufzurufen.

    Outer 0=new Outer(); Outer.Inner i= O.new Inner();

  6. innerhalb der inneren Klasse Dieser Zeiger auf die innere Klasse.

    this.member-current inner class outerclassname.this--outer class

  7. Für die innere Klasse gilt der Modifikator - public, default,

    final,abstract,strictfp,+private,protected,static

  8. Outer $ Inner ist der Name des inneren Klassennamens.

  9. innere Klasse innerhalb der Instanzmethode, dann können wir auf das statische Feld und das Instanzfeld der äußeren Klasse zugreifen.

10.inner Klasse innerhalb der statischen Methode, dann können wir nur auf das statische Feld von zugreifen

äußere Klasse.

class outer{

    int x=10;
    static int y-20;

    public void m1() {
        int i=30;
        final j=40;

        class inner{

            public void m2() {
                // have accees x,y and j
            }
        }
    }
}
Gaurav Tiwari
quelle