java variabili classe - Perché tentare di stampare una variabile non inizializzata non genera sempre un messaggio di errore





3 Answers

Leggendo il JLS, la risposta sembra essere nella sezione 16.2.2 :

Un campo membro vuoto vuoto V è definitivamente assegnato (e inoltre non è definitivamente non assegnato) prima del blocco (§14.2) che è il corpo di qualsiasi metodo nell'ambito di V e prima della dichiarazione di qualsiasi classe dichiarata nell'ambito di V

Ciò significa che quando viene chiamato un metodo, il campo finale viene assegnato al suo valore predefinito 0 prima di richiamarlo, quindi quando lo si fa riferimento all'interno del metodo, esso viene compilato correttamente e viene stampato il valore 0.

Tuttavia, quando si accede al campo al di fuori di un metodo, viene considerato non assegnato, quindi l'errore di compilazione. Anche il seguente codice non verrà compilato:

public class Main {
    final int x;
    {
        method();
        System.out.println(x);
        x = 7;
    }
    void method() { }
    public static void main(String[] args) { }
}

perché:

  • V è [un] assegnato prima di ogni altra istruzione S del blocco iff V è [un] assegnato dopo l'istruzione immediatamente precedente a S nel blocco.

Poiché il campo finale x non è assegnato prima dell'invocazione del metodo, non è ancora assegnato dopo.

Questa nota nel JLS è anche rilevante:

Nota che non ci sono regole che ci permettano di concludere che V è definitivamente non assegnato prima del blocco che è il corpo di qualsiasi costruttore, metodo, inizializzatore di istanze o inizializzatore statico dichiarato in C Possiamo concludere informalmente che V non è definitivamente non assegnato prima del blocco che è il corpo di qualsiasi costruttore, metodo, inizializzatore di istanze o inizializzatore statico dichiarato in C, ma non è necessario che tale regola venga dichiarata esplicitamente.

d'istanza tipi definire

Alcuni potrebbero trovarlo simile alla domanda SO Le variabili finali di Java avranno valori predefiniti? ma questa risposta non risolve completamente questo problema, poiché quella domanda non stampa direttamente il valore di x all'interno del blocco di inizializzazione dell'istanza.

Il problema sorge quando provo a stampare x direttamente all'interno del blocco di inizializzazione dell'istanza, pur avendo assegnato un valore a x prima della fine del blocco:

Caso 1

class HelloWorld {

    final int x;

    {
        System.out.println(x);
        x = 7;
        System.out.println(x);    
    }

    HelloWorld() {
        System.out.println("hi");
    }

    public static void main(String[] args) {
        HelloWorld t = new HelloWorld();
    }
}

Questo dà un errore di compilazione che indica che la variabile x potrebbe non essere stata inizializzata.

$ javac HelloWorld.java
HelloWorld.java:6: error: variable x might not have been initialized
        System.out.println(x);
                           ^
1 error

Caso 2

Invece di stampare direttamente, sto chiamando una funzione per stampare:

class HelloWorld {

    final int x;

    {
        printX();
        x = 7;
        printX();
    }

    HelloWorld() {
        System.out.println("hi");
    }

    void printX() {
        System.out.println(x);
    }

    public static void main(String[] args) {
        HelloWorld t = new HelloWorld();
    }
}

Questo compila correttamente e dà l'output

0
7
hi

Qual è la differenza concettuale tra i due casi?




Ok, ecco i miei 2 centesimi.

Sappiamo tutti che le variabili finali possono essere inizializzate solo durante la dichiarazione o successivamente nei costruttori. Tenendo presente questo fatto, vediamo cosa è successo qui finora.

Nessun errore Caso:

Quindi quando usi un metodo, ha già un valore.

 1) If you initialize it, that value.
 2) If not, the default value of data type. 

Errore:

Quando lo fai in un blocco di inizializzazione, che stai vedendo errori.

Se guardi i docs of initialization block

{
    // whatever code is needed for initialization goes here
}

e

Il compilatore Java copia i blocchi di inizializzazione in ogni costruttore. Pertanto, questo approccio può essere utilizzato per condividere un blocco di codice tra più costruttori.

All'occhio del compilatore, il tuo codice è letteralmente uguale a

class HelloWorld {

    final int x;
    HelloWorld() {
        System.out.println(x);  ------------ ERROR here obviously
        x = 7;
        System.out.println(x);  
        System.out.println("hi");
    }

    public static void main(String[] args) {
        HelloWorld t = new HelloWorld();
    }
}

Lo stai usando prima ancora di inizializzarlo.




Trattiamo qui con il blocco di inizializzazione. Il compilatore Java copia i blocchi di inizializzazione in ogni costruttore.

L'errore del compilatore non si verifica nel secondo esempio, poiché la stampa di x è in un altro frame, fare riferimento alle specifiche.




Related