Warum können wir in einer nicht statischen inneren Klasse keine statische Methode haben?
Wenn ich die innere Klasse statisch mache, funktioniert es. Warum?
static
.")
Warum können wir in einer nicht statischen inneren Klasse keine statische Methode haben?
Wenn ich die innere Klasse statisch mache, funktioniert es. Warum?
static
.")
Antworten:
Da eine Instanz einer inneren Klasse implizit einer Instanz ihrer äußeren Klasse zugeordnet ist, kann sie selbst keine statischen Methoden definieren. Da eine statisch verschachtelte Klasse nicht direkt auf Instanzvariablen oder Methoden verweisen kann, die in ihrer umschließenden Klasse definiert sind, kann sie nur über eine Objektreferenz verwendet werden. Es ist sicher, statische Methoden in einer statisch verschachtelten Klasse zu deklarieren.
A.B.sync(X)
oder sogar (aus A heraus) B.sync(x)
?
Es macht nicht viel Sinn, eine statische Methode in einer nicht statischen inneren Klasse zuzulassen. Wie würden Sie darauf zugreifen? Sie können (zumindest anfänglich) nicht auf eine nicht statische Instanz der inneren Klasse zugreifen, ohne eine Instanz der äußeren Klasse zu durchlaufen. Es gibt keine rein statische Möglichkeit, eine nicht statische innere Klasse zu erstellen.
Für eine äußere Klasse Outer
können Sie auf eine statische Methode test()
wie die folgende zugreifen :
Outer.test();
Für eine statische innere Klasse Inner
können Sie wie folgt auf ihre statische Methode zugreifen innerTest()
:
Outer.Inner.innerTest();
Wenn dies Inner
jedoch nicht statisch ist, gibt es jetzt keine rein statische Möglichkeit, auf die Methode zu verweisen innertest
. Nicht statische innere Klassen sind an eine bestimmte Instanz ihrer äußeren Klasse gebunden. Eine Funktion unterscheidet sich von einer Konstanten darin, dass ein Verweis auf Outer.Inner.CONSTANT
garantiert so eindeutig ist, wie es ein Funktionsaufruf Outer.Inner.staticFunction();
nicht ist. Angenommen, Sie haben Inner.staticFunction()
diese Anrufe getState()
, die in definiert sindOuter
. Wenn Sie versuchen, diese statische Funktion aufzurufen, haben Sie jetzt einen mehrdeutigen Verweis auf die innere Klasse. Das heißt, auf welcher Instanz der inneren Klasse rufen Sie die statische Funktion auf? Es ist wichtig. Es gibt keine wirklich statische Möglichkeit, auf diese statische Methode zu verweisen, da implizit auf das äußere Objekt verwiesen wird.
Paul Bellora hat Recht, dass die Sprachdesigner dies hätten zulassen können. Sie müssten dann in statischen Methoden der nicht statischen inneren Klasse jeglichen Zugriff auf den impliziten Verweis auf die äußere Klasse sorgfältig untersagen. Welchen Wert hat es an dieser Stelle, eine innere Klasse zu sein, wenn Sie nur statisch auf die äußere Klasse verweisen können? Und wenn der statische Zugriff in Ordnung ist, warum nicht die gesamte innere Klasse als statisch deklarieren? Wenn Sie einfach die innere Klasse selbst statisch machen, haben Sie keinen impliziten Verweis auf die äußere Klasse, und Sie haben diese Mehrdeutigkeit nicht mehr.
Wenn Sie tatsächlich benötigen statische Methoden auf einer nicht-statische innere Klasse, dann müssen Sie wahrscheinlich Ihr Design zu überdenken.
Outer.Inner i = new Outer().new Inner();
auch innere Klassen sind erlaubt zu statisch deklarieren Konstanten nach JLS §15.28.
Outer.Inner.staticMethod()
so wie Sie zugreifen können Outer.Inner.CONSTANT
. "Sie können nicht auf ... eine nicht statische Instanz der inneren Klasse zugreifen, ohne eine Instanz der äußeren Klasse zu durchlaufen." Warum brauchen Sie eine Instanz? Sie brauchen keine Instanz von, Outer
um anzurufen Outer.staticMethod()
. Ich weiß, dass dies nicht pingelig ist, aber mein Punkt ist, dass es keinen Sinn macht, Ihre Antwort so zu formulieren. IMHO hätten die Sprachdesigner es zulassen können, wenn sie wollten.
Outer.Inner.CONSTANT
und Outer.Inner.staticMethod()
besteht darin, dass eine Referenz auf eine Konstante keine Chance hat, implizit auf die Instanz zu verweisen, Outer
in der sie Inner
instanziiert wurde. Alle Referenzen haben Outer.staticMethod()
denselben exakten Status. Alle Referenzen haben Outer.Inner.CONSTANT
denselben exakten Status. Verweise auf Outer.Inner.staticMethod()
sind jedoch nicht eindeutig: Der "statische" Zustand ist aufgrund des impliziten Verweises auf die äußere Klasse in jeder Instanz von nicht wirklich statisch Inner
. Es gibt keine wirklich eindeutige, statische Möglichkeit, darauf zuzugreifen.
Outer.this
. Ich stimme den Java-Sprachdesignern zu, dass es keinen Grund gibt, statische Methoden oder nicht endgültige statische Felder in inneren Klassen zuzulassen, da sich alles in einer inneren Klasse im Kontext der einschließenden Klasse befinden sollte.
Ich habe eine Theorie, die richtig sein kann oder nicht.
Zunächst sollten Sie einige Dinge darüber wissen, wie innere Klassen in Java implementiert werden. Angenommen, Sie haben diese Klasse:
class Outer {
private int foo = 0;
class Inner implements Runnable {
public void run(){ foo++; }
}
public Runnable newFooIncrementer(){ return new Inner(); }
}
Wenn Sie es kompilieren, sieht der generierte Bytecode so aus, als hätten Sie Folgendes geschrieben:
class Outer {
private int foo = 0;
static class Inner implements Runnable {
private final Outer this$0;
public Inner(Outer outer){
this$0 = outer;
}
public void run(){ this$0.foo++; }
}
public Runnable newFooIncrementer(){ return new Inner(this); }
}
Wenn wir nun statische Methoden in nicht statischen inneren Klassen zulassen, möchten Sie möglicherweise so etwas tun.
class Outer {
private int foo = 0;
class Inner {
public static void incrFoo(){ foo++; }
}
}
... was ziemlich vernünftig aussieht, da die Inner
Klasse eine Inkarnation pro Outer
Instanz zu haben scheint . Aber wie wir oben gesehen haben, sind die nicht statischen inneren Klassen wirklich nur syntaktischer Zucker für statische "innere" Klassen, so dass das letzte Beispiel ungefähr gleichbedeutend wäre mit:
class Outer {
private int foo = 0;
static class Inner {
private final Outer this$0;
public Inner(Outer outer){
this$0 = outer;
}
public static void incrFoo(){ this$0.foo++; }
}
}
... was eindeutig nicht funktioniert, da this$0
es nicht statisch ist. Auf diese Weise wird erklärt, warum statische Methoden nicht zulässig sind (obwohl Sie argumentieren könnten, dass Sie statische Methoden zulassen könnten, solange sie nicht auf das umschließende Objekt verweisen) und warum Sie keine nicht endgültigen statischen Felder haben können ( Es wäre kontraintuitiv, wenn Instanzen nicht statischer innerer Klassen von verschiedenen Objekten den "statischen Zustand" teilen würden. Es erklärt auch , warum letzte Felder sind erlaubt (solange sie das einschließende Objekt nicht verweisen).
public static double sinDeg(double theta) { ... }
eine innere Mathe-Dienstprogrammklasse schreiben wollte ?
Der einzige Grund ist "kein Muss". Warum also die Mühe machen, es zu unterstützen?
Syntaktisch gibt es keinen Grund, einer inneren Klasse zu verbieten, statische Mitglieder zu haben. Obwohl eine Instanz von Inner
einer Instanz von zugeordnet ist Outer
, ist es dennoch möglich, Outer.Inner.myStatic
ein statisches Mitglied von zu referenzieren, Inner
wenn Java dies beschließt.
Wenn Sie etwas für alle Instanzen von Inner
freigeben müssen, können Sie es einfach Outer
als statische Elemente einfügen. Dies ist nicht schlimmer, als wenn Sie statische Mitglieder verwenden, in Inner
denen Sie Outer
weiterhin auf jedes private Mitglied von zugreifen könnenInner
ohnehin zugegriffen werden kann (verbessert die Kapselung nicht).
Wenn Sie etwas für alle Inner
von einem outer
Objekt erstellten Instanzen freigeben müssen, ist es sinnvoller, sie zu platzierenOuter
als normale Mitglieder Klasse einzuteilen.
Ich stimme der Meinung nicht zu, dass "eine statisch verschachtelte Klasse so ziemlich nur eine Klasse der obersten Ebene ist". Ich denke, es ist besser, eine statisch verschachtelte Klasse / innere Klasse wirklich als Teil der äußeren Klasse zu betrachten, da sie auf die privaten Mitglieder der äußeren Klasse zugreifen können. Und Mitglieder der äußeren Klasse sind auch "Mitglieder der inneren Klasse". Es ist also nicht erforderlich, statische Elemente in der inneren Klasse zu unterstützen. Ein gewöhnliches / statisches Mitglied in der äußeren Klasse wird ausreichen.
Von: https://docs.oracle.com/javase/tutorial/java/javaOO/nested.html
Wie bei Instanzmethoden und -variablen ist eine innere Klasse einer Instanz ihrer einschließenden Klasse zugeordnet und hat direkten Zugriff auf die Methoden und Felder dieses Objekts. Da eine innere Klasse einer Instanz zugeordnet ist, kann sie auch keine statischen Elemente selbst definieren.
Die Erklärung von Oracle ist oberflächlich und handgewellt. Da es keinen technischen oder syntaktischen Grund gibt, statische Mitglieder innerhalb einer inneren Klasse zu verhindern (dies ist in anderen Sprachen wie C # zulässig), war die Motivation der Java-Designer wahrscheinlich konzeptioneller Geschmack und / oder eine Frage der technischen Bequemlichkeit.
Hier ist meine Spekulation:
Im Gegensatz zu Klassen der obersten Ebene sind innere Klassen instanzabhängig: Eine Instanz der inneren Klasse ist einer Instanz jeder ihrer äußeren Klassen zugeordnet und hat direkten Zugriff auf ihre Mitglieder. Dies ist die Hauptmotivation, sie in Java zu haben. Anders ausgedrückt: Eine innere Klasse ist zur Instanziierung im Kontext einer äußeren Klasseninstanz gedacht . Ohne eine Instanz der äußeren Klasse sollte eine innere Klasse nicht besser verwendbar sein als die anderen Instanzmitglieder der äußeren Klasse. Nennen wir dies den instanzabhängigen Geist der inneren Klassen.
Die Natur der statischen Elemente (die NICHT objektorientiert sind) kollidiert mit der instanzabhängigen Geist innerer Klassen (die objektorientiert sind), da Sie ein statisches Element einer inneren Klasse ohne eine Instanz einer äußeren Klasse durch referenzieren / aufrufen können unter Verwendung des qualifizierten inneren Klassennamens.
Insbesondere statische Variablen können auf noch eine andere Weise beleidigen: Zwei Instanzen einer inneren Klasse, die verschiedenen Instanzen der äußeren Klasse zugeordnet sind, würden statische Variablen gemeinsam nutzen. Da Variablen eine Komponente des Status sind, teilen sich die beiden Instanzen der inneren Klasse praktisch den Status unabhängig von den Instanzen der äußeren Klasse, denen sie zugeordnet sind. Es ist nicht inakzeptabel, dass statische Variablen auf diese Weise funktionieren (wir akzeptieren sie in Java als sinnvollen Kompromiss für die OOP-Reinheit), aber es gibt wohl ein tieferes Vergehen, wenn sie in inneren Klassen zugelassen werden, deren Instanzen bereits mit Instanzen der äußeren Klasse gekoppelt sind von Entwurf. Das Verbot statischer Mitglieder innerhalb der inneren Klassen zugunsten des instanzabhängigen Geistes erntet den zusätzlichen Vorteil, diese tiefere OOP-Straftat zu verhindern.
Andererseits ist eine solche Straftat nicht mit statischen Konstanten verbunden, die keinen sinnvollen Zustand darstellen und daher zulässig sind. Warum nicht statische Konstanten verbieten, um maximale Übereinstimmung mit dem instanzabhängigen Geist zu erzielen ? Vielleicht, weil Konstanten nicht mehr Speicher als nötig beanspruchen müssen (wenn sie nicht statisch sein müssen, werden sie in jede Instanz der inneren Klasse kopiert, was möglicherweise verschwenderisch ist). Ansonsten kann ich mir den Grund für die Ausnahme nicht vorstellen.
Es mag keine solide Argumentation sein, aber IMO macht es den größten Sinn aus der flüchtigen Bemerkung von Oracle in dieser Angelegenheit.
Kurze Antwort: Das mentale Modell, das die meisten Programmierer für die Funktionsweise von Scope haben, ist nicht das von Javac verwendete Modell. Die Anpassung an das intuitivere Modell hätte eine große Änderung der Funktionsweise von Javac erforderlich gemacht.
Der Hauptgrund, warum statische Elemente in inneren Klassen wünschenswert sind, ist die Sauberkeit des Codes - ein statisches Element, das nur von einer inneren Klasse verwendet wird, sollte darin leben, anstatt in der äußeren Klasse platziert zu werden. Erwägen:
class Outer {
int outID;
class Inner {
static int nextID;
int id = nextID++;
String getID() {
return outID + ":" + id;
}
}
}
Überlegen Sie, was in getID () vor sich geht, wenn ich den nicht qualifizierten Bezeichner "outID" verwende. Der Bereich, in dem diese Kennung angezeigt wird, sieht ungefähr so aus:
Outer -> Inner -> getID()
Auch hier umfasst die "Outer" -Ebene des Bereichs sowohl statische als auch Instanzmitglieder von Outer, da dies genau die Funktionsweise von Javac ist. Dies ist verwirrend, da wir normalerweise aufgefordert werden, den statischen Teil einer Klasse als eine weitere Ebene des Geltungsbereichs zu betrachten:
Outer static -> Outer instance -> instanceMethod()
\----> staticMethod()
Auf diese Weise kann staticMethod () natürlich nur statische Mitglieder von Outer sehen. Wenn javac jedoch so funktioniert, würde das Verweisen auf eine Instanzvariable in einer statischen Methode zu dem Fehler "Name kann nicht behoben werden" führen. Was wirklich passiert, ist, dass der Name im Gültigkeitsbereich gefunden wird, aber dann eine zusätzliche Überprüfungsebene aktiviert wird und herausfindet, dass der Name in einem Instanzkontext deklariert wurde und aus einem statischen Kontext referenziert wird.
OK, wie hängt das mit inneren Klassen zusammen? Naiv denken wir, dass es keinen Grund gibt, warum innere Klassen keinen statischen Bereich haben können, weil wir uns den Bereich so vorstellen:
Outer static -> Outer instance -> Inner instance -> getID()
\------ Inner static ------^
Mit anderen Worten, statische Deklarationen in der inneren Klasse und Instanzdeklarationen in der äußeren Klasse sind beide im Instanzkontext der inneren Klasse gültig, aber keine dieser Deklarationen ist tatsächlich in der anderen verschachtelt. beide sind stattdessen im statischen Bereich von Outer verschachtelt.
So funktioniert javac einfach nicht - es gibt eine einzige Ebene für den Bereich sowohl für statische als auch für Instanzmitglieder, und der Bereich ist immer streng verschachtelt. Sogar die Vererbung wird implementiert, indem Deklarationen in die Unterklasse kopiert werden, anstatt den Bereich der Oberklasse zu verzweigen und zu durchsuchen.
Um statische Mitglieder innerer Klassen zu unterstützen, müsste javac entweder statische Bereiche und Instanzbereiche aufteilen und Verzweigungs- und Wiederverbindungsbereichshierarchien unterstützen, oder es müsste seine einfache boolesche Idee des "statischen Kontexts" erweitern, um den Kontexttyp auf allen Ebenen zu verfolgen der verschachtelten Klasse im aktuellen Bereich.
Warum können wir in einer nicht statischen inneren Klasse keine statische Methode haben?
Hinweis: Eine nicht statisch verschachtelte Klasse wird als innere Klasse bezeichnet, sodass Sie sie nicht non-static inner class
als solche haben.
Eine innere Klasseninstanz existiert nicht ohne eine entsprechende Instanz der äußeren Klasse. Eine innere Klasse kann keine anderen statischen Elemente als Kompilierungszeitkonstanten deklarieren. Wenn es erlaubt wäre, hätte es Unklarheiten über die Bedeutung von gegeben static
. In diesem Fall hätte es gewisse Verwirrungen gegeben:
Aus diesem Grund haben die Designer wahrscheinlich beschlossen, dieses Problem überhaupt nicht zu behandeln.
Wenn ich die innere Klasse statisch mache, funktioniert es. Warum ?
Auch hier können Sie eine innere Klasse nicht statisch machen, sondern eine statische Klasse als verschachtelt deklarieren. In diesem Fall ist diese verschachtelte Klasse tatsächlich Teil der äußeren Klasse und kann ohne Probleme statische Elemente haben.
Dieses Thema hat bei vielen Aufmerksamkeit erregt, dennoch werde ich versuchen, es auf einfachste Weise zu erklären.
Zunächst wird unter Bezugnahme auf http://docs.oracle.com/javase/specs/jls/se7/html/jls-12.html#jls-12.4.1 eine Klasse oder Schnittstelle unmittelbar vor dem ersten Auftreten / Aufruf initialisiert eines Mitglieds, dem das statische Schlüsselwort vorangestellt ist.
Wenn wir uns also mit einem statischen Element innerhalb einer inneren Klasse abfinden, führt dies zur Initialisierung der inneren Klasse, nicht unbedingt der äußeren / umschließenden Klasse. Wir behindern also die Klasseninitialisierungssequenz.
Berücksichtigen Sie auch die Tatsache, dass eine nicht statische innere Klasse der Instanz einer einschließenden / äußeren Klasse zugeordnet ist. Die Zuordnung zu einer Instanz bedeutet also, dass die innere Klasse innerhalb einer Instanz der äußeren Klasse vorhanden ist und sich zwischen den Instanzen unterscheidet.
Um den Punkt zu vereinfachen, benötigen wir für den Zugriff auf das statische Element eine Instanz einer äußeren Klasse, aus der wir erneut eine Instanz einer nicht statischen inneren Klasse erstellen müssen. Statische Mitglieder sollten nicht an Instanzen gebunden sein, daher erhalten Sie einen Kompilierungsfehler.
Eine innere Klasse unterscheidet sich grundlegend von einer statisch verschachtelten Klasse, obwohl beide in der Syntax ähnlich sind. Statisch verschachtelte Klassen sind nur ein Mittel zur Gruppierung, während innere Klassen eine starke Assoziation - und Zugriff auf alle Werte - ihrer äußeren Klasse haben. Sie sollten sicher sein, warum Sie eine innere Klasse verwenden möchten, und dann sollte es ziemlich natürlich sein, welche Sie verwenden müssen. Wenn Sie eine statische Methode deklarieren müssen, handelt es sich wahrscheinlich um eine statisch verschachtelte Klasse, die Sie sowieso möchten.
Angenommen, es gibt zwei Instanzen der äußeren Klasse und beide haben die innere Klasse instanziiert. Wenn die innere Klasse nun ein statisches Element hat, wird nur eine Kopie dieses Elements im Heap-Bereich gespeichert. In diesem Fall verweisen beide Objekte der äußeren Klasse darauf Einzelkopie & sie können es zusammen ändern. Dies kann zu einer "Dirty Read" -Situation führen, um zu verhindern, dass Java diese Einschränkung angewendet hat. Eine weitere Stärke, die dieses Argument unterstützt, ist, dass Java hier endgültige statische Mitglieder zulässt, deren Werte nicht sein können geändert von einem der äußeren Klassenobjekte. Bitte lassen Sie mich, wenn ich falsch liege.
Zunächst einmal, warum jemand das statische Element in einer nicht statischen inneren Klasse definieren möchte? Die Antwort lautet, damit das äußere Klassenmitglied diese statischen Elemente nur mit dem inneren Klassennamen verwenden kann. Richtig?
In diesem Fall können wir das Mitglied in der äußeren Klasse direkt definieren. Dies wird allen Objekten der inneren Klasse innerhalb der äußeren Klasseninstanz zugeordnet.
wie unten Code,
public class Outer {
class Inner {
public static void method() {
}
}
}
kann so geschrieben werden
public class Outer {
void method() {
}
class Inner {
}
}
Meiner Meinung nach lässt der Java-Designer diese Funktionalität nicht zu, um den Code nicht zu komplizieren. Andernfalls wird diese Funktionalität möglicherweise in zukünftigen Versionen mit einigen weiteren Funktionen angezeigt.
Versuchen Sie, die Klasse als normales Feld zu behandeln, dann werden Sie verstehen.
//something must be static. Suppose something is an inner class, then it has static keyword which means it's a static class
Outer.something
Es ist sinnlos, Mitglieder der inneren Klasse als statisch zu haben, da Sie überhaupt nicht darauf zugreifen können.
Denken Sie darüber nach, um auf ein statisches Mitglied zuzugreifen, verwenden Sie className.memberName. In unserem Fall sollte es so etwas wie OuterclassName.innerclassName.memberName sein. Jetzt sehen Sie, warum Innerclass statisch sein muss.