Warum verbietet Java statische Felder in inneren Klassen?

85
class OuterClass {
 class InnerClass {
  static int i = 100; // compile error
  static void f() { } // compile error
 }
} 

Obwohl es nicht möglich ist, mit auf das statische Feld OuterClass.InnerClass.izuzugreifen, wäre es hilfreich, dieses Feld statisch zu machen, wenn ich etwas aufzeichnen möchte, das statisch sein sollte, z. B. die Anzahl der erstellten InnerClass-Objekte. Also warum nicht Java verbieten statische Felder / Methoden in inneren Klassen?

BEARBEITEN: Ich weiß, wie man den Compiler mit statisch verschachtelter Klasse (oder statischer innerer Klasse) glücklich macht, aber ich möchte wissen, warum Java statische Felder / Methoden innerhalb innerer Klassen (oder gewöhnlicher innerer Klasse) sowohl vom Sprachdesign als auch vom Sprachdesign verbietet Implementierungsaspekte, wenn jemand mehr darüber weiß.

Jichao
quelle
3
Mein Lieblingsbeispiel ist ein Logger nur für die innere Klasse. Es kann nicht statisch sein wie alle anderen Logger.
Piotr Findeisen

Antworten:

31

Die Idee hinter inneren Klassen besteht darin, im Kontext der einschließenden Instanz zu arbeiten. Irgendwie widerspricht das Zulassen statischer Variablen und Methoden dieser Motivation?

8.1.2 Innere Klassen und umschließende Instanzen

Eine innere Klasse ist eine verschachtelte Klasse, die nicht explizit oder implizit als statisch deklariert ist. Innere Klassen dürfen keine statischen Initialisierer (§8.7) oder Mitgliedsschnittstellen deklarieren. Innere Klassen dürfen keine statischen Elemente deklarieren, es sei denn, es handelt sich um Konstantenfelder zur Kompilierungszeit (§15.28).

Gregory Pakosz
quelle
18
Vielleicht ist es nur entschieden wie die
Gregory Pakosz
3
Sie können das nicht statische Innere nicht ohne eine übergeordnete Referenz instanziieren , aber Sie können es trotzdem initialisieren .
Skaffman
Wenn die ClassLoader einen Cache mit der Aufschrift "Class X wurde initialisiert" behalten, kann ihre Logik nicht zum Initialisieren mehrerer Instanzen von Class [Objekten, die] X darstellen, verwendet werden. Dies ist erforderlich, wenn Class-Objekte als innere Klassen in mehreren instanziiert werden müssen verschiedene Objekte).
Erwin Smout
@skaffman Es macht immer noch keinen Sinn. Die statischen Eigenschaften der inneren Klasse werden nur einmal initialisiert. Was wäre also das Problem? Im Moment habe ich eine statische Hashmap und ich habe ungefähr 4 Methoden, die nur diese Map manipulieren, was es idealer macht, alles in einer inneren Klasse zu gruppieren. Allerdings müsste die statische Hashmap jetzt draußen leben und möglicherweise auch andere Dinge, die einfach nur dumm sind. Was wäre das Problem bei der Initialisierung statischer Eigenschaften?
mmm
54

Ich möchte wissen, warum Java statische Felder / Methoden innerhalb innerer Klassen verbietet

Weil diese inneren Klassen innere "Instanz" -Klassen sind. Das heißt, sie sind wie ein Instanzattribut des umschließenden Objekts.

Da es sich um "Instanz" -Klassen handelt, ist es nicht sinnvoll, staticFunktionen zuzulassen , da staticdiese zunächst ohne Instanz funktionieren sollen.

Es ist, als würden Sie gleichzeitig versuchen, ein statisches / Instanzattribut zu erstellen.

Nehmen Sie das folgende Beispiel:

class Employee {
    public String name;
}

Wenn Sie zwei Instanzen von Mitarbeitern erstellen:

Employee a = new Employee(); 
a.name = "Oscar";

Employee b = new Employee();
b.name = "jcyang";

Es ist klar, warum jeder seinen eigenen Wert für die Immobilie hat name, oder?

Das gleiche passiert mit der inneren Klasse; Jede innere Klasseninstanz ist unabhängig von der anderen inneren Klasseninstanz.

Wenn Sie also versuchen, ein counterKlassenattribut zu erstellen , können Sie diesen Wert nicht auf zwei verschiedene Instanzen verteilen.

class Employee {
    public String name;
    class InnerData {
        static count; // ??? count of which ? a or b? 
     }
}

Was wäre beim Erstellen der Instanz aund bim obigen Beispiel ein korrekter Wert für die statische Variable count? Es ist nicht möglich, es zu bestimmen, weil die Existenz derInnerData Klasse vollständig von jedem der umschließenden Objekte abhängt.

Deshalb staticbraucht die Klasse, wenn sie als deklariert wird , keine lebende Instanz mehr, um sich selbst zu leben. Da keine Abhängigkeit mehr besteht, können Sie ein statisches Attribut frei deklarieren.

Ich denke, das klingt wiederholt, aber wenn Sie über die Unterschiede zwischen Instanz- und Klassenattributen nachdenken, ist dies sinnvoll.

OscarRyz
quelle
4
Ich werde Ihre Erklärung für statische Eigenschaften der inneren Klasse kaufen , aber wie @skaffman in einem Kommentar zu meiner Antwort hervorhebt, was ist mit statischen Methoden ? Es scheint, als ob Methoden erlaubt sein sollten, ohne zu verlangen, dass sie von irgendeiner Instanz getrennt werden. In Java können Sie statische Methoden für Instanzen aufrufen (obwohl dies als schlechter Stil angesehen wird). BTW: Ich fragte einen Kollegen zu versuchen , die OPs Code als C # zu kompilieren und es tut der Kompilierung. C # erlaubt dies anscheinend, was zeigt, dass das, was das OP tun möchte, nicht gegen ein grundlegendes OO-Prinzip verstößt.
Asaph
2
Passiert genauso mit den Methoden. Der Punkt hier ist nicht die Attribute oder Methoden , die statis sind oder nicht, sondern die Tatsache, dass die innere Klasse selbst Instanz "Zeug" ist. Ich meine, das Problem hier ist, dass eine Instanz einer solchen inneren Klasse erst existiert, wenn die äußere Klasse erstellt wurde. Daher, welche Methode würde dann versendet, wenn es nichts gibt. Sie würden nur Luft schlagen, weil Sie an erster Stelle eine Instanz benötigen.
OscarRyz
1
Über C # ... na ja. Es ist nicht OO-gültig, nur weil C # es erlaubt, ich meine nicht, dass es falsch ist, aber C # enthält mehrere Paradigmen, um die Entwicklung auch auf Kosten der Konsistenz zu vereinfachen (Sie müssen mit jeder .NET-Version neue Dinge lernen) und es erlaubt dies und andere Arten von Dingen unter anderem. Ich denke das ist eine gute Sache. Wenn die Community der Meinung ist, dass eine zusätzliche Funktion cool genug ist, kann C # sie in Zukunft haben.
OscarRyz
2
@OscarRyz Warum benötigen Sie Instanzen der inneren Klasse, um ihre statischen Methoden / Felder zu verwenden? Ein Beispiel für eine [nützliche] statische Methode in der inneren Klasse ist die private Hilfsmethode.
Leonid Semyonov
1
Bei Verwendung von finalsind statische Felder in der inneren Klasse in Java zulässig. Wie erklären Sie dieses Szenario?
Nummer 945
34

InnerClasskann keine staticMitglieder haben, da es zu einer Instanz (von OuterClass) gehört. Wenn Sie erklären , InnerClasswie statices von der Instanz zu lösen, wird Ihr Code kompilieren.

class OuterClass {
    static class InnerClass {
        static int i = 100; // no compile error
        static void f() { } // no compile error
    }
}

Übrigens: Sie können weiterhin Instanzen von erstellen InnerClass. staticin diesem Zusammenhang erlaubt dies, dass ohne eine einschließende Instanz von OuterClass.

Asaph
quelle
6
InnerClassgehört nicht dazu OuterClass, Instanzen davon tun. Die beiden Klassen selbst haben keine solche Beziehung. Die Frage, warum Sie keine statischen Methoden verwenden können, InnerClassbleibt bestehen.
Skaffman
9

Tatsächlich können Sie statische Felder deklarieren, wenn sie Konstanten sind und in Kompilierungszeit geschrieben werden.

class OuterClass {
    void foo() {
        class Inner{
            static final int a = 5; // fine
            static final String s = "hello"; // fine
            static final Object o = new Object(); // compile error, because cannot be written during compilation
        }
    }
}
vmolchanov
quelle
8
  1. Klasse Die Initialisierungssequenz ist ein kritischer Grund.

Da innere Klassen von der Instanz der einschließenden / äußeren Klasse abhängig sind, muss die äußere Klasse vor der Initialisierung der inneren Klasse initialisiert werden.
Dies sagt JLS über die Klasseninitialisierung. Der Punkt, den wir brauchen, ist, dass Klasse T initialisiert wird, wenn

  • Ein von T deklariertes statisches Feld wird verwendet und das Feld ist keine konstante Variable.

Wenn also auf die innere Klasse ein statisches Feld zugegriffen wird, wird die innere Klasse initialisiert, dies stellt jedoch nicht sicher, dass die einschließende Klasse initialisiert wird.

  1. Es würde einige Grundregeln verletzen . Sie können zum letzten Abschnitt (zu two cases) springen, um Noob-Sachen zu vermeiden

Eine Sache , wenn einige sindstatic nested classnested classstatic wird es sich verhalten wie eine normale Klasse in jeder Hinsicht , und es wird mit der Außenklasse zugeordnet.

Aber das Konzept von Inner class/ ist es wird mit dem der äußeren / einschließenden Klasse assoziiert . Bitte beachten Sie, dass die Instanz nicht der Klasse zugeordnet ist. Die Zuordnung zur Instanz bedeutet nun eindeutig, dass sie ( nach dem Konzept der Instanzvariablen ) innerhalb einer Instanz vorhanden ist und sich zwischen den Instanzen unterscheidet. non-static nested classinstance

Wenn wir nun etwas statisch machen, erwarten wir, dass es beim Laden der Klasse initialisiert wird und von allen Instanzen gemeinsam genutzt werden sollte. Aber um nicht statisch zu sein, werden selbst innere Klassen ( Sie können die Instanz der inneren Klasse vorerst definitiv vergessen ) nicht mit allen Instanzen der äußeren / umschließenden Klasse geteilt ( zumindest konzeptionell ). Wie können wir dann diese Variable erwarten? der inneren Klasse wird unter allen Instanzen der inneren Klasse geteilt.

Wenn Java es uns erlaubt, statische Variablen in nicht statisch verschachtelten Klassen zu verwenden. Es wird zwei Fälle geben .

  • Wenn es mit der gesamten Instanz der inneren Klasse geteilt wird, verstößt es gegen das Konzept von context of instance(Instanzvariable). Dann ist es ein NEIN.
  • Wenn es nicht für alle Instanzen freigegeben wird, verstößt es gegen das Konzept der Statik. Wieder NEIN.
Saif
quelle
5

Hier ist die Motivation, die ich für diese "Grenze" am besten geeignet finde: Sie können das Verhalten eines statischen Feldes einer inneren Klasse als Instanzfeld des äußeren Objekts implementieren; Sie benötigen also keine statischen Felder / Methoden . Das Verhalten, das ich meine, ist, dass alle Instanzen der inneren Klasse eines Objekts ein Feld (oder eine Methode) gemeinsam haben.

Angenommen, Sie möchten alle Instanzen der inneren Klasse zählen, dann würden Sie Folgendes tun:

public class Outer{
    int nofInner; //this will count the inner class 
                  //instances of this (Outer)object
                  //(you know, they "belong" to an object)
    static int totalNofInner; //this will count all 
                              //inner class instances of all Outer objects
    class Inner {
        public Inner(){
            nofInner++;
            totalNofInner++;
        }
    }
}
ianos
quelle
2
Aber die Frage ist, was ist der Grund dafür, statische Felder zuzulassen, wenn sie dann deklariert werden final?
Trost
Wenn Sie sich [ stackoverflow.com/a/1954119/1532220] ansehen (OscarRyzs Antwort oben): Seine Motivation ist, dass der Variablen kein Wert zugeordnet werden kann. Wenn die Variable endgültig ist, können Sie natürlich ganz einfach wissen, welchen Wert Sie zuweisen müssen (Sie müssen wissen).
Ianos
2

Mit einfachen Worten, nicht statische innere Klassen sind Instanzvariablen für die äußere Klasse und werden nur erstellt, wenn eine äußere Klasse und ein äußeres Klassenobjekt zur Laufzeit erstellt werden, während statische Variablen zum Laden der Klasse erstellt werden. Nicht statische innere Klasse ist also Laufzeitsache, deshalb ist statisch nicht der Teil einer nicht statischen inneren Klasse.

HINWEIS: Behandeln Sie innere Klassen immer wie eine Variable für eine äußere Klasse. Sie können statisch oder nicht statisch sein wie alle anderen Variablen.

Mannu
quelle
Aber die innere Klasse kann static finalKonstanten haben.
Raining
1

Weil es Mehrdeutigkeiten im Sinne von "statisch" verursachen würde.

Innere Klassen können keine anderen statischen Elemente als Konstanten zur Kompilierungszeit deklarieren. Es würde eine Unklarheit über die Bedeutung von "statisch" geben. Bedeutet dies, dass es nur eine Instanz in der virtuellen Maschine gibt? Oder nur eine Instanz pro äußerem Objekt? Die Sprachdesigner beschlossen, dieses Problem nicht anzugehen.

Entnommen aus "Core Java SE 9 für Ungeduldige" von Cay S. Horstmann. S. 90 Kapitel 2.6.3

aj hrishikesh
quelle
-1

Ich denke, es ist für die Konsistenz. Obwohl es keine technischen Einschränkungen zu geben scheint, können Sie von außen nicht auf statische Mitglieder der internen Klasse zugreifen, dh OuterClass.InnerClass.iweil der mittlere Schritt nicht statisch ist.

Chochos
quelle
Aber die innere Klasse kann static finalKonstanten haben.
Raining