Perché Java proibisce i campi statici nelle classi interne?


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

Sebbene non sia possibile accedere al campo statico con OuterClass.InnerClass.i, se voglio registrare qualcosa che dovrebbe essere statico, ad esempio il numero di oggetti InnerClass creati, sarebbe utile rendere quel campo statico. Allora perché Java proibisce campi / metodi statici nelle classi interne?

EDIT: So come rendere felice il compilatore con la classe annidata statica (o classe interna statica), ma quello che voglio sapere è perché java proibisce i campi / metodi statici all'interno delle classi interne (o ordinaria classe interna) sia dal design del linguaggio che aspetti di implementazione, se qualcuno ne sa di più.


3
Il mio esempio preferito è avere un logger solo per la classe interna. Non può essere statico come lo sono tutti gli altri logger.
Piotr Findeisen

Poiché Java 16, poiché non è più il caso, vedere questa risposta .
Nicolai Parlog

Risposte:


32

L'idea alla base delle classi interne è di operare nel contesto dell'istanza di inclusione. In qualche modo, consentire variabili e metodi statici contraddice questa motivazione?

8.1.2 Classi interne e istanze di contenimento

Una classe interna è una classe annidata che non è dichiarata statica in modo esplicito o implicito. Le classi interne non possono dichiarare inizializzatori statici (§8.7) o interfacce membro. Le classi interne non possono dichiarare membri statici, a meno che non siano campi costanti in fase di compilazione (§15.28).


19
forse è solo deciso così
Gregory Pakosz

3
non puoi istanziare l'interno non statico senza un riferimento genitore, ma puoi comunque inizializzarlo .
skaffman

Se i ClassLoader mantengono una cache che dice "La Classe X è stata inizializzata", la loro logica non può essere utilizzata per inizializzare più istanze di Class [oggetti che rappresentano] X (che è ciò che è necessario quando gli oggetti Class devono essere istanziati come classi interne all'interno di più oggetti distinti).
Erwin Smout

@skaffman Ancora non ha senso. Le proprietà statiche della classe interna verranno inizializzate solo una volta, quindi quale sarebbe il problema? In questo momento, ho una hashmap statica e ho circa 4 metodi che manipolano solo questa mappa, rendendola più ideale per raggruppare tutto in una classe interna. Tuttavia, l'hashmap statica ora dovrebbe vivere fuori, e forse altre cose correlate, il che è semplicemente stupido. Quale sarebbe il problema con l'inizializzazione delle proprietà statiche?
mmm

Poiché Java 16, poiché non è più il caso, vedere questa risposta .
Nicolai Parlog

56

quello che voglio sapere è perché java proibisce i campi / metodi statici all'interno delle classi interne

Perché quelle classi interne sono classi interne "istanza". Cioè, sono come un attributo di istanza dell'oggetto che lo racchiude.

Dal momento che sono classi di "istanza", non ha alcun senso consentire le staticfunzionalità, poiché staticè destinato a funzionare senza un'istanza in primo luogo.

È come se provassi a creare un attributo statico / istanza allo stesso tempo.

Prendiamo il seguente esempio:

class Employee {
    public String name;
}

Se crei due istanze di dipendente:

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

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

È chiaro perché ognuno ha il suo valore per la proprietà name, giusto?

Lo stesso accade con la classe interna; ogni istanza della classe interna è indipendente dall'altra istanza della classe interna.

Pertanto, se si tenta di creare un counterattributo di classe, non è possibile condividere tale valore tra due istanze diverse.

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

Quando crei l'istanza ae bnell'esempio sopra, quale sarebbe un valore corretto per la variabile statica count? Non è possibile determinarlo, perché l'esistenza della InnerDataclasse dipende completamente da ciascuno degli oggetti che la racchiudono.

Ecco perché, quando la classe è dichiarata come static, non ha più bisogno di un'istanza vivente, per vivere se stessa. Ora che non c'è dipendenza, puoi dichiarare liberamente un attributo statico.

Penso che questo suoni ripetitivo, ma se pensi alle differenze tra attributi di istanza e attributi di classe, avrà senso.


4
Comprerò la tua spiegazione per le proprietà statiche della classe interna, ma come sottolinea @skaffman in un commento alla mia risposta, che dire dei metodi statici ? Sembra che i metodi dovrebbero essere consentiti senza imporre che siano staccati da qualsiasi istanza. In effetti, in java, puoi chiamare metodi statici sulle istanze (sebbene sia considerato uno stile non corretto). BTW: ho chiesto a un collega per tentare di compilare il codice del PO come C # e lo fa compilazione. Quindi C # apparentemente lo consente, il che dimostra che ciò che l'OP vuole fare non viola alcun principio fondamentale dell'OO.
Asaph

2
Succede esattamente lo stesso con i metodi. Il punto qui non è che gli attributi oi metodi siano statis o no, ma il fatto che la classe interna stessa sia l' istanza "roba". Voglio dire, il problema qui è che un'istanza di tale classe interna non esiste fino a quando non viene creata la classe esterna. Quindi quale metodo verrebbe inviato se non c'è nulla. Colpiresti l'aria solo perché avrai bisogno di un'istanza in primo luogo.
OscarRyz

1
A proposito di C # ... beh. Non è OO valido solo perché C # lo consente, non voglio dire che sia sbagliato, ma C # include diversi paradigmi per rendere lo sviluppo più facile anche a costo di coerenza (devi imparare cose nuove con ogni versione .NET) e consente questo e altri tipi di cose tra le altre. Penso che sia una buona cosa. Se la comunità ritiene che una funzionalità extra sia abbastanza interessante, C # potrebbe averla in futuro.
OscarRyz

2
@OscarRyz Perché hai bisogno di istanze della classe interna per usare i suoi metodi / campi statici ? E un esempio di metodo statico [utile] nella classe interna è il metodo di supporto privato.
Leonid Semyonov

1
Con l'uso di final, i campi statici sono consentiti nella classe interna in java. Come spieghi questo scenario?
Numero 945

34

InnerClassnon può avere staticmembri perché appartiene a un'istanza (di OuterClass). Se si dichiara InnerClasscome staticper staccarlo dalla esempio, il codice verrà compilato.

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

BTW: sarai comunque in grado di creare istanze di InnerClass. staticin questo contesto consente che ciò accada senza un'istanza racchiusa di OuterClass.


6
InnerClassnon non appartiene OuterClass, le istanze di esso fanno. Le due classi stesse non hanno tale relazione. La domanda sul motivo per cui non si possono avere metodi statici InnerClassrimane ancora.
skaffman

10

In realtà, puoi dichiarare campi statici se sono costanti e vengono scritti in fase di compilazione.

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
        }
    }
}

8
  1. La sequenza di inizializzazione della classe è un motivo critico.

Poiché le classi interne dipendono dall'istanza della classe di inclusione / esterna, la classe esterna deve essere inizializzata prima dell'inizializzazione della classe interna.
Questo è JLS dice sull'inizializzazione della classe. Il punto di cui abbiamo bisogno è che la classe T verrà inizializzata se

  • Viene utilizzato un campo statico dichiarato da T e il campo non è una variabile costante.

Quindi, se la classe interna ha un campo statico che accede, ciò causerà l'inizializzazione della classe interna, ma ciò non garantirà che la classe che lo racchiude sia inizializzata.

  1. Viola alcune regole di base . puoi saltare all'ultima sezione (a two cases) per evitare roba da noob

Una cosa su , quando alcuni sono si comporterà proprio come una normale classe in ogni modo ed è associata alla classe Outer.static nested classnested classstatic

Ma il concetto di Inner class/ è sarà associato alla classe di esterno / inclusione. Nota associato all'istanza non alla classe. Ora l'associazione con l'istanza significa chiaramente che ( dal concetto di variabile di istanza ) esisterà all'interno di un'istanza e sarà diversa tra le istanze. non-static nested classinstance

Ora, quando creiamo qualcosa di statico, ci aspettiamo che venga inizializzato quando la classe viene caricata e dovrebbe essere condivisa tra tutte le istanze. Ma per essere non statiche, anche le classi interne stesse ( puoi sicuramente dimenticare l'istanza della classe interna per ora ) non sono condivise con tutte le istanze della classe esterna / inclusiva ( almeno concettualmente ), allora come possiamo aspettarci che qualche variabile della classe interna sarà condivisa tra tutte le istanze della classe interna.

Quindi, se Java ci consente di utilizzare una variabile statica all'interno di una classe annidata non statica. ci saranno due casi .

  • Se è condiviso con tutte le istanze della classe interna violerà il concetto di context of instance(variabile istanza). Allora è un NO.
  • Se non è condiviso con tutte le istanze, violerà il concetto di staticità. Ancora una volta NO.

5

Ecco la motivazione che trovo più adatta a questo "limite": è possibile implementare il comportamento di un campo statico di una classe interna come campo istanza dell'oggetto esterno; Quindi non hai bisogno di campi / metodi statici . Il comportamento che intendo è che tutte le istanze di classi interne di alcuni oggetti condividono un campo (o metodo).

Quindi, supponiamo di voler contare tutte le istanze della classe interna, faresti:

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++;
        }
    }
}

2
Ma la domanda è: qual è la ragione per consentire i campi statici quando vengono dichiarati finalallora?
Solace

se guardi [ stackoverflow.com/a/1954119/1532220] (risposta di OscarRyz sopra): La sua motivazione è che un valore non può essere associato alla variabile. Ovviamente, se la variabile è finale, puoi facilmente sapere quale valore assegnare (devi saperlo).
ianos

2

In parole semplici, le classi interne non statiche sono variabili di istanza per la classe esterna e vengono create solo quando viene creata una classe esterna e un oggetto della classe esterna viene creato in fase di esecuzione mentre le variabili statiche vengono create durante il caricamento della classe. Quindi la classe interna non statica è una cosa di runtime, ecco perché statica non fa parte di una classe interna non statica.

NOTA: tratta le classi interne sempre come una variabile per una classe esterna, possono essere statiche o non statiche come qualsiasi altra variabile.


Ma la classe interna può avere static finalcostanti.
Pioggia il


1

Perché provocherebbe ambiguità nel significato di "statico".

Le classi interne non possono dichiarare membri statici diversi dalle costanti in fase di compilazione. Ci sarebbe un'ambiguità sul significato di "statico". Significa che c'è solo un'istanza nella macchina virtuale? O solo un'istanza per oggetto esterno? I progettisti del linguaggio hanno deciso di non affrontare questo problema.

Tratto da "Core Java SE 9 for the Impatient" di Cay S. Horstmann. Pg 90 Capitolo 2.6.3


0

Da Java 16 in poi, non è più così. Citando da JEP 395 (sulla finalizzazione dei record):

Allenta la restrizione di vecchia data per cui una classe interna non può dichiarare un membro che è esplicitamente o implicitamente statico. Ciò diventerà legale e, in particolare, consentirà a una classe interna di dichiarare un membro che è una classe record.

Infatti, il seguente codice può essere compilato con Java 16 (provato con 16.ea.27):

public class NestingClasses {

    public class NestedClass {

        static final String CONSTANT = new String(
                "DOES NOT COMPILE WITH JAVA <16");

        static String constant() {
            return CONSTANT;
        }

    }

}

-1

Immagino sia per coerenza. Anche se non sembra esserci alcuna limitazione tecnica, non saresti in grado di accedere ai membri statici della classe interna dall'esterno, cioè OuterClass.InnerClass.iperché il passaggio intermedio non è statico.


Ma la classe interna può avere static finalcostanti.
Pioggia il
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.