Perché non possiamo avere un metodo statico in una classe interna (non statica)?


142

Perché non possiamo avere un metodo statico in una classe interna non statica?

Se rendo statica la classe interna, funziona. Perché?


4
Perché ora Java è quel vecchio COBOL :)
ses

La linea di fondo è: perché non l'hanno ancora implementata.
intrepidis,

1
"Interno non statico" è una tautologia.
Marchese di Lorne,

Se non vuoi esporre la tua classe interiore ad altri e speri che contenga metodi statici, puoi mettere i modificatori sia "privati" che "statici" sulla classe interna.
Willie Z,

Una classe interna non è per definizione statica. È possibile avere una classe nidificata / membro statica e una classe nidificata / membro non statica. Quest'ultimo è anche conosciuto come una classe interiore. (Riferimento: la sezione 8.1.3 del JLS afferma "Una classe interna è una classe nidificata che non viene dichiarata in modo esplicito o implicito static.")
Erwin Bolwidt,

Risposte:


111

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.


7
So che una classe interna è associata a un'istanza della sua classe esterna e so che è un po 'inutile che diventiamo in grado di dichiarare membri statici all'interno di una classe interna ma sto ancora chiedendo perché non una classe interna può dichiarare membri statici?
Kareem,

15
In C ++ puoi avere, quindi questo è un bug nel linguaggio Java.
Antidepressivo industriale

39
La parola bug ... Non penso che la parola significhi ciò che pensi che significhi.
Seth Nelson,

24
Una frase più appropriata sarebbe "fastidioso come un figlio di puttana". Non capisco perché Java non lo consenta. A volte, voglio che una classe interna utilizzi le proprietà della classe genitore, ma mantenga metodi statici per una migliore spaziatura dei nomi. C'è qualcosa di intrinsecamente sbagliato in questo? :(
Angad,

6
Esattamente. Voglio scrivere una classe interna di utilità. Alcuni dei suoi metodi trarrebbero beneficio dall'accesso alla classe esterna, quindi non posso renderlo statico, ma alcuni dei suoi metodi sono solo funzioni di utilità. Perché non riesco a chiamare A.B.sync(X)o anche (dall'interno di A) B.sync(x)?
Edward Falk,

44

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 Innernon è 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.


6
-1 Non sono d'accordo con l'angolazione che hai preso qui. Certamente possiamo fare riferimento a un tipo di classe interna, ad esempio Outer.Inner i = new Outer().new Inner();Inoltre, alle classi interne è consentito dichiarare costanti statiche secondo JLS § 15.28.
Paul Bellora,

2
Sì, le classi interne possono dichiarare costanti statiche . Non ha nulla a che fare con i metodi statici! Sebbene sia possibile fare riferimento a un metodo statico non staticamente, questo è sconsigliato. Tutti gli strumenti di qualità del codice si lamentano per quel tipo di riferimento e per una buona ragione. E ti sei perso il punto. Non ho mai detto che non c'è modo di fare riferimento a una classe interna statica. Ho detto che non esiste un modo STATICO di fare riferimento al metodo statico di una classe interna di una classe esterna non statica. Pertanto, non esiste un modo PROPER per fare riferimento a esso.
Eddie,

25
"Non ha molto senso consentire un metodo statico in una classe interna non statica; come accederesti?" Chiameresti 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 Outerchiamare 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.
Paul Bellora,

1
La differenza tra Outer.Inner.CONSTANTe Outer.Inner.staticMethod()è che un riferimento a una costante non ha alcuna possibilità di fare riferimento implicito all'istanza Outerin cui è Innerstata istanziata. Tutti i riferimenti per Outer.staticMethod()condividere lo stesso stato esatto. Tutti i riferimenti per Outer.Inner.CONSTANTcondividere 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.
Eddie,

3
@Eddie Non è possibile fare riferimento ai campi dell'istanza in un metodo statico, quindi non vi sono conflitti relativi all'impossibilità di fare riferimento al campo dell'istanza implicita 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.
Theodore Murdock,

20

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 Innerclasse sembra avere una incarnazione per Outeristanza. 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$0non è 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).


7
Ma questo è solo un normale errore di tipo "tentativo di accedere a una variabile non statica da un contesto statico" - non diverso dal caso in cui un metodo statico di livello superiore tenti di accedere alla propria variabile di istanza della propria classe.
Lawrence Dol,

2
Mi piace questa risposta perché in realtà spiega perché non è tecnicamente possibile, anche se potrebbe sembrare sintatticamente possibile.
LoPoBo,

@gustafc, penso che sia stata un'ottima spiegazione. Ma come sottolinea Lawrence, è solo un fallimento a causa del riferimento a foo, che non è statico. E se volessi scrivere public static double sinDeg(double theta) { ... }una lezione di utilità matematica interna?
Edward Falk,

6

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 Innersia associata a un'istanza di Outer, è comunque possibile utilizzare Outer.Inner.myStaticper 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 Outercome 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 Innercreate da un outeroggetto, 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.


Neanche le classi interne sono un "must". Tuttavia, come il linguaggio non fornisce classi interne, dovrebbe fornire una completa implementazione e significativa di loro.
intrepidis,

4

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.


3

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.


Penso che una difficoltà più fondamentale nel consentire alle classi interne non statiche di avere membri statici non costanti sia che i programmatori che dichiarano che tali membri potrebbero avere l'intenzione di vincolarli alle istanze della classe esterna o che siano veramente statici. Nei casi in cui un costrutto - se legale - potrebbe essere sensibilmente specificato come significato di una delle due cose diverse e in cui entrambe queste cose possono essere espresse in altri modi non ambigui, specificare che il costrutto come illegale è spesso meglio che specificarlo come avere entrambi i significati.
supercat

3

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 classtale.

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:

  1. Significa che esiste solo un'istanza nella VM?
  2. O solo un'istanza per oggetto esterno?

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.


3

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.

  1. 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.

  2. 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.


2

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.


Benedikt, cosa intendi quando dici "le classi nidificate statiche sono solo un mezzo per raggruppare"?
Ankur,

0

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.


0

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ù.


0

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 

-1

È 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 ....


-2

Sono consentiti metodi statici su classi nidificate statiche. Per esempio

public class Outer {

  public static class Inner {

    public static void method() {

    }
  }
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.