Perché non possiamo avere un metodo statico in una classe interna non statica?
Se rendo statica la classe interna, funziona. Perché?
static
.")
Perché non possiamo avere un metodo statico in una classe interna non statica?
Se rendo statica la classe interna, funziona. Perché?
static
.")
Risposte:
Poiché un'istanza di una classe interna è implicitamente associata a un'istanza della sua classe esterna, non può definire alcun metodo statico stesso. Poiché una classe nidificata statica non può fare riferimento direttamente alle variabili di istanza o ai metodi definiti nella sua classe che la racchiude, può usarle solo attraverso un riferimento a un oggetto, è sicuro dichiarare metodi statici in una classe nidificata statica.
A.B.sync(X)
o anche (dall'interno di A) B.sync(x)
?
Non ha molto senso consentire un metodo statico in una classe interna non statica; come accederesti? Non è possibile accedere (almeno inizialmente) a un'istanza di classe interna non statica senza passare attraverso un'istanza di classe esterna. Non esiste un modo puramente statico per creare una classe interna non statica.
Per una classe esterna Outer
, puoi accedere a un metodo statico test()
come questo:
Outer.test();
Per una classe interna statica Inner
, puoi accedere al suo metodo statico in innerTest()
questo modo:
Outer.Inner.innerTest();
Tuttavia, se Inner
non è statico, ora non esiste un modo puramente statico per fare riferimento al metodo innertest
. Le classi interne non statiche sono legate a un'istanza specifica della loro classe esterna. Una funzione è diversa da una costante, in quanto un riferimento a Outer.Inner.CONSTANT
è garantito come non ambiguo in un modo in cui Outer.Inner.staticFunction();
non lo è una chiamata di funzione . Supponiamo che tu abbia Inner.staticFunction()
quelle chiamate getState()
, che è definito in Outer
. Se provi a invocare quella funzione statica, ora hai un riferimento ambiguo alla classe interna. Cioè, su quale istanza della classe interna invochi la funzione statica? Importa. Vedi, non esiste un modo veramente statico di fare riferimento a quel metodo statico, a causa del riferimento implicito all'oggetto esterno.
Paul Bellora ha ragione sul fatto che i progettisti del linguaggio avrebbero potuto permetterlo. Dovrebbero quindi impedire con attenzione qualsiasi accesso al riferimento implicito alla classe esterna nei metodi statici della classe interna non statica. A questo punto, che valore ha questa classe interna se non puoi fare riferimento alla classe esterna, se non staticamente? E se l'accesso statico va bene, perché non dichiarare statica l'intera classe interna? Se semplicemente rendi statica la classe interna stessa, allora non hai alcun riferimento implicito alla classe esterna e non hai più questa ambiguità.
Se in realtà hai bisogno di metodi statici su una classe interna non statica, probabilmente dovrai ripensare il tuo progetto.
Outer.Inner i = new Outer().new Inner();
Inoltre, alle classi interne è consentito dichiarare costanti statiche secondo JLS § 15.28.
Outer.Inner.staticMethod()
proprio come puoi accedere Outer.Inner.CONSTANT
. "Non puoi accedere a ... un'istanza di classe interna non statica senza passare attraverso un'istanza di classe esterna." Perché avresti bisogno di un'istanza? Non hai bisogno di un'istanza di Outer
chiamare Outer.staticMethod()
. So che è nitido ma il mio punto è che non ha senso inquadrare la tua risposta in questo modo. IMHO i progettisti linguistici avrebbero potuto permetterlo se lo avessero voluto.
Outer.Inner.CONSTANT
e Outer.Inner.staticMethod()
è che un riferimento a una costante non ha alcuna possibilità di fare riferimento implicito all'istanza Outer
in cui è Inner
stata istanziata. Tutti i riferimenti per Outer.staticMethod()
condividere lo stesso stato esatto. Tutti i riferimenti per Outer.Inner.CONSTANT
condividere lo stesso stato esatto. Tuttavia, i riferimenti a Outer.Inner.staticMethod()
sono ambigui: lo stato "statico" non è veramente statico, a causa del riferimento implicito alla classe esterna in ciascuna istanza di Inner
. Non esiste un modo veramente inequivocabile e statico per accedervi.
Outer.this
. Concordo con i progettisti del linguaggio Java sul fatto che non vi è alcun motivo per consentire metodi statici o campi statici non finali nelle classi interne, perché tutto in una classe interna dovrebbe essere all'interno del contesto della classe che lo racchiude.
Ho una teoria, che può essere o non essere corretta.
Innanzitutto, dovresti sapere alcune cose su come le classi interne sono implementate in Java. Supponiamo di avere questa classe:
class Outer {
private int foo = 0;
class Inner implements Runnable {
public void run(){ foo++; }
}
public Runnable newFooIncrementer(){ return new Inner(); }
}
Quando lo compili, il bytecode generato sembrerà come se avessi scritto qualcosa del genere:
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); }
}
Ora, se consentissimo metodi statici in classi interne non statiche, potresti voler fare qualcosa del genere.
class Outer {
private int foo = 0;
class Inner {
public static void incrFoo(){ foo++; }
}
}
... che sembra abbastanza ragionevole, poiché la Inner
classe sembra avere una incarnazione per Outer
istanza. Ma come abbiamo visto sopra, le classi interne non statiche sono in realtà solo zucchero sintattico per classi "interne" statiche, quindi l'ultimo esempio sarebbe approssimativamente equivalente a:
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++; }
}
}
... che chiaramente non funzionerà, poiché this$0
non è statico. Questo tipo di spiegazione spiega perché i metodi statici non sono consentiti (anche se è possibile sostenere che è possibile consentire metodi statici purché non facciano riferimento all'oggetto che lo racchiude) e perché non è possibile avere campi statici non finali ( sarebbe controintuitivo se le istanze di classi interne non statiche di oggetti diversi condividessero "stato statico"). Spiega anche perché i campi finali sono consentiti (purché non facciano riferimento all'oggetto racchiuso).
public static double sinDeg(double theta) { ... }
una lezione di utilità matematica interna?
L'unica ragione è "non un must", quindi perché preoccuparsi di supportarlo?
Sintatticamente, non vi è alcun motivo per vietare a una classe interna di avere membri statici. Sebbene un'istanza di Inner
sia associata a un'istanza di Outer
, è comunque possibile utilizzare Outer.Inner.myStatic
per fare riferimento a un membro statico diInner
se java decide di farlo.
Se devi condividere qualcosa tra tutte le istanze di Inner
, puoi semplicemente inserirli Outer
come membri statici. Questo non è peggio di quando si utilizzano membri statici Inner
, in cui Outer
è ancora possibile accedere a qualsiasi membro privato diInner
(non migliora l'incapsulamento).
Se hai bisogno di condividere qualcosa tra tutte le istanze Inner
create da un outer
oggetto, ha più senso inserirleOuter
classe come membri ordinari.
Non condivido l'opinione secondo cui "una classe nidificata statica è praticamente solo una classe di primo livello". Penso che sia meglio considerare davvero una classe nidificata statica / classe interna come parte della classe esterna, perché possono accedere ai membri privati della classe esterna. E anche i membri della classe esterna sono "membri della classe interna". Quindi non è necessario supportare un membro statico nella classe interna. Sarà sufficiente un membro ordinario / statico nella classe esterna.
Da: https://docs.oracle.com/javase/tutorial/java/javaOO/nested.html
Come per i metodi e le variabili di istanza, una classe interna è associata a un'istanza della sua classe chiusa e ha accesso diretto ai metodi e ai campi di quell'oggetto. Inoltre, poiché una classe interna è associata a un'istanza, non può definire alcun membro statico stesso.
La spiegazione di Oracle è superficiale e fatta a mano. Poiché non esiste alcun motivo tecnico o sintattico per impedire i membri statici all'interno di una classe interna (è consentito in altri linguaggi come C #), la motivazione dei progettisti Java era probabilmente un gusto concettuale e / o una questione di convenienza tecnica.
Ecco la mia speculazione:
A differenza delle classi di livello superiore, le classi interne dipendono dall'istanza: un'istanza di classe interna è associata a un'istanza di ciascuna delle sue classi esterne e ha accesso diretto ai loro membri. Questa è la motivazione principale per averli in Java. Espresso in un altro modo: una classe interna è pensata per l' istanza nel contesto di un'istanza di classe esterna. Senza un'istanza di classe esterna, una classe interna non dovrebbe essere più utilizzabile rispetto agli altri membri dell'istanza della classe esterna. Facciamo riferimento a questo come lo spirito dipendente dall'istanza delle classi interiori.
La natura stessa dei membri statici (che NON sono orientati agli oggetti) si scontra con l' istanza dipendente spirito delle classi interne (che è orientato agli oggetti) perché è possibile fare riferimento / chiamare un membro statico di una classe interna senza un'istanza di classe esterna da utilizzando il nome della classe interna qualificata.
Le variabili statiche in particolare possono offendere l'ennesimo modo: due istanze di una classe interna associate a diverse istanze della classe esterna condividono le variabili statiche. Poiché le variabili sono un componente di stato, le due istanze di classe interne in effetti condividerebbero lo stato indipendentemente dalle istanze di classe esterne a cui sono associate. Non è inaccettabile che le variabili statiche funzionino in questo modo (le accettiamo in Java come un ragionevole compromesso per la purezza di OOP), ma c'è probabilmente un'offesa più profonda permettendo loro in classi interne le cui istanze sono già accoppiate con istanze di classe esterne di progettazione. Proibire i membri statici all'interno delle classi interne a favore dello spirito dipendente dall'istanza raccoglie il vantaggio aggiuntivo di anticipare questo attacco OOP più profondo.
D'altra parte, nessuna tale offesa è implicata da costanti statiche, che non costituiscono in modo significativo stato e quindi sono consentite. Perché non proibire le costanti statiche per la massima coerenza con lo spirito dipendente dall'istanza ? Forse perché le costanti non devono occupare più memoria del necessario (se sono costrette a essere non statiche, vengono copiate in ogni istanza di classe interna potenzialmente dannosa). Altrimenti non riesco a immaginare il motivo dell'eccezione.
Potrebbe non essere un ragionamento solido come la roccia, ma l'IMO ha il massimo senso dell'osservazione superficiale di Oracle sulla questione.
Risposta breve: il modello mentale della maggior parte dei programmatori di come funziona l'oscilloscopio non è il modello utilizzato da javac. Abbinare il modello più intuitivo avrebbe richiesto un grande cambiamento al funzionamento di javac.
Il motivo principale per cui sono desiderabili membri statici nelle classi interne è la pulizia del codice: un membro statico utilizzato solo da una classe interna dovrebbe vivere al suo interno, piuttosto che dover essere inserito nella classe esterna. Tener conto di:
class Outer {
int outID;
class Inner {
static int nextID;
int id = nextID++;
String getID() {
return outID + ":" + id;
}
}
}
Considera cosa sta succedendo in getID () quando uso l'identificatore non qualificato "outID". L'ambito in cui appare questo identificatore è simile al seguente:
Outer -> Inner -> getID()
Anche in questo caso, poiché questo è il modo in cui funziona javac, il livello "Esterno" dell'ambito include sia membri statici che di istanza di Outer. Questo è confuso perché ci viene generalmente detto di pensare alla parte statica di una classe come a un altro livello dell'ambito:
Outer static -> Outer instance -> instanceMethod()
\----> staticMethod()
In questo modo di pensare, ovviamente staticMethod () può vedere solo i membri statici di Outer. Ma se fosse così che funziona javac, fare riferimento a una variabile di istanza in un metodo statico comporterebbe un errore "impossibile risolvere il nome". Quello che succede realmente è che il nome si trova nell'ambito, ma poi entra in gioco un ulteriore livello di check e scopre che il nome è stato dichiarato in un contesto di istanza e viene referenziato da un contesto statico.
OK, come si collega alle classi interne? Ingenuamente, pensiamo che non vi siano ragioni per cui le classi interne non possano avere un ambito statico, perché stiamo immaginando che l'ambito funzioni in questo modo:
Outer static -> Outer instance -> Inner instance -> getID()
\------ Inner static ------^
In altre parole, le dichiarazioni statiche nella classe interna e le dichiarazioni di istanza nella classe esterna sono entrambe nell'ambito nell'ambito del contesto di istanza della classe interna, ma nessuna di queste è effettivamente nidificata nell'altra; entrambi sono invece nidificati nell'ambito statico di Outer.
Non è così che funziona javac: esiste un unico livello di ambito sia per i membri statici che per quelli di istanza e l'ambito è sempre strettamente nidificato. Anche l'ereditarietà viene implementata copiando le dichiarazioni nella sottoclasse anziché ramificando e cercando l'ambito della superclasse.
Per supportare i membri statici delle classi interne, javac dovrebbe dividere gli ambiti statici e di istanza e supportare la gerarchia di ramificazioni e ricongiungimenti degli ambiti, oppure dovrebbe estendere la sua semplice idea booleana di "contesto statico" per cambiare per tenere traccia del tipo di contesto a tutti i livelli di classe nidificata nell'ambito corrente.
Perché non possiamo avere un metodo statico in una classe interna non statica?
Nota: una classe nidificata non statica è nota come classe interna, quindi non è presente non-static inner class
tale.
Un'istanza di classe interna non ha esistenza senza un'istanza corrispondente di classe esterna. Una classe interna non può dichiarare membri statici diversi dalle costanti di tempo di compilazione. Se fosse stato permesso, ci sarebbe stata ambiguità sul significato di static
. In quel caso ci sarebbero state alcune confusioni:
Questo è il motivo per cui i progettisti probabilmente hanno preso la decisione di non gestire affatto questo problema.
Se rendo statica la classe interna, funziona. Perché ?
Ancora una volta non è possibile rendere statica una classe interna, piuttosto è possibile dichiarare una classe statica come nidificata. In tal caso questa classe nidificata fa effettivamente parte della classe esterna e può avere membri statici senza alcun problema.
Questo argomento ha attirato l'attenzione di molti, cercherò comunque di spiegare nel modo più semplice.
In primo luogo, con riferimento a http://docs.oracle.com/javase/specs/jls/se7/html/jls-12.html#jls-12.4.1 , una classe o un'interfaccia viene inizializzata immediatamente prima della prima occorrenza / invocazione di qualsiasi membro preceduto dalla parola chiave statica.
Quindi, se tolleriamo un membro statico all'interno di una classe interna, ciò porterà all'inizializzazione della classe interna, non necessariamente della classe esterna / chiusa. Quindi, ostacoliamo la sequenza di inizializzazione della classe.
Considera anche il fatto che una classe interna non statica è associata all'istanza di una classe chiusa / esterna. Quindi, associarsi a un'istanza significherà che la classe interna esisterà all'interno di un'istanza della classe esterna e sarà diversa tra le istanze.
Semplificando il punto, per accedere al membro statico abbiamo bisogno di un'istanza di una classe esterna, dalla quale avremo di nuovo bisogno di creare un'istanza di classe interna non statica. I membri statici non devono essere associati alle istanze e pertanto viene visualizzato un errore di compilazione.
Una classe interna è qualcosa di completamente diverso da una classe nidificata statica sebbene entrambi siano simili nella sintassi. Le classi nidificate statiche sono solo un mezzo per raggruppare, mentre le classi interne hanno una forte associazione - e l'accesso a tutti i valori della - classe esterna. Dovresti essere sicuro del motivo per cui vuoi usare una classe interiore e poi dovrebbe diventare abbastanza naturale quale devi usare. Se devi dichiarare un metodo statico, probabilmente è comunque una classe nidificata statica che desideri.
supponiamo che ci siano due istanze di classe esterna ed entrambe abbiano un'istanza di classe interna. Ora se la classe interna ha un membro statico, manterrà solo una copia di quel membro nell'area dell'heap. In questo caso entrambi gli oggetti della classe esterna faranno riferimento a questo copia singola e possono modificarlo insieme. Ciò può causare una situazione di "lettura sporca", quindi per evitare che Java abbia applicato questa limitazione. Un altro punto di forza per supportare questo argomento è che java consente qui i membri statici finali, quelli i cui valori non possono essere modificato da uno degli oggetti della classe esterna. Per favore fatemi se sbaglio.
Prima di tutto perché qualcuno vuole definire il membro statico in una classe interna non statica? la risposta è, in modo che il membro della classe esterno possa usare quei membri statici solo con il nome della classe interna, giusto?
Ma per questo caso possiamo definire direttamente il membro nella classe esterna. che sarà associato a tutti gli oggetti della classe interna, all'interno dell'istanza della classe esterna.
come sotto il codice,
public class Outer {
class Inner {
public static void method() {
}
}
}
può essere scritto così
public class Outer {
void method() {
}
class Inner {
}
}
Quindi secondo me non complicare il codice java designer non consente questa funzionalità o potremmo vederla nelle versioni future con alcune funzionalità in più.
Prova a trattare la classe come un campo normale, quindi capirai.
//something must be static. Suppose something is an inner class, then it has static keyword which means it's a static class
Outer.something
È inutile avere membri della classe interna come statici perché non sarai in grado di accedervi in primo luogo.
Pensaci, per accedere a un membro statico usi className.memberName ,, nel nostro caso, dovrebbe essere qualcosa come outerclassName.innerclassName.memberName ,,, ora vedi perché innerclass deve essere statico ....