2013-04-04 12 views
35

questo non riesce a compilare (con un errore illegal forward reference), come ci si aspetterebbe:L'inizializzatore ricorsivo funziona quando aggiungo "questo"?

class test { 
    int x = x + 42; 
} 

Ma questo funziona:

class test { 
    int x = this.x + 42; 
} 

Cosa sta succedendo? Cosa viene assegnato in quest'ultimo caso?

+10

Che strano ... (+1) – NPE

+4

Ricordami di: while (true) {try {return; } infine {continua; }} – devconsole

risposta

17

Riepilogo: entrambi gli inizializzatori accedono a un campo che deve ancora essere inizializzato (e quindi ha ancora il valore predefinito di zero). Poiché questo è probabilmente un errore di programmazione, la lingua vieta alcune forme semplici di tale accesso. Tuttavia, non vieta la forma più complessa.

Il comportamento è conforme alla JLS, specificamente §8.3.2.3. Restrictions on the use of Fields during Initialization

La dichiarazione dell'utente deve comparire testualmente prima di essere utilizzato solo se l'elemento è un (rispettivamente static) campo di una classe o interfaccia istanza C e tutte le seguenti condizioni:

  • L'utilizzo avviene in un'istanza (rispettivamente static) inizializzazione variabile di C o in un caso (rispettivamente static) inizializzatore di C.

  • L'utilizzo non è sul lato sinistro di un compito.

  • L'utilizzo avviene tramite un nome semplice.

  • C è la classe più interna o l'interfaccia che racchiude l'utilizzo.

Il primo esempio soddisfa le quattro condizioni ed è quindi valido. Il secondo esempio non soddisfa la terza condizione (this.x non è un nome semplice) ed è quindi OK.

La sequenza complessiva degli eventi è la seguente:

Pertanto, se un inizializzatore fa riferimento a un campo che appare in seguito nella definizione della classe (o al campo stesso), vedrebbe il valore predefinito dell'altro campo. Questo potrebbe essere un errore di programmazione ed è quindi esplicitamente vietato dal §8.3.2.3.

Se aggirare §8.3.2.3 da, ad esempio, utilizzando this. per inoltrare-fare riferimento a un campo, you'll see the default value (zero per int). Così il seguente è ben definito ed è garantita per impostare x-42:

class test { 
    int x = this.x + 42; 
} 
+0

@nneonneo: Penso di aver capito la logica dietro questo. Si prega di consultare la mia risposta aggiornata. – NPE

18

E 'troppo difficile da scoprire e vietare tutti gli accessi al x durante l'inizializzazione di x.Ad esempio

int x = that().x;    | int x = getX(); 
           | 
Test that(){ return this; }  | int getX(){ return x; } 

La specifica si interrompe in "accesso con un nome semplice" e non tenta di essere più completa.

In un'altra sezione, "Assegnazione definita", la specifica fa la stessa cosa. Ad esempio

public class Test 
{ 
    static final int y; 
    static final int z = y; // fail, y is not definitely assigned 
    static{ y = 1; } 
} 

public class Test 
{ 
    static final int y; 
    static final int z = Test.y; // pass... because it's not a simple name 
    static{ y = 1; } 
} 

interessante, "Assegnazione Definite" menziona specificamente che this.x equivale a x

(o, per un campo, il semplice nome del campo qualificato da questo)

questa clausola potrebbe essere aggiunta anche alla sezione indicata da NPE.

  • l'utilizzo avviene tramite un semplice nome (o un nome semplice qualificato da questo)

Ma, alla fine, non è possibile al momento della compilazione di analizzare tutti usi possibili/accessi in un campo

+0

Avete una vista se 'int x = this.x + 42;' ha semantica ben definita? – NPE

+0

probabilmente anche la specifica dovrebbe vietarlo, come "l'utilizzo avviene tramite un nome semplice o un nome semplice qualificato da questo" – ZhongYu

1

Nel primo caso il compilatore tenta di valutare l'espressione 'x + 42' ma non riesce perché x non è inizializzato.

Nel secondo caso l'espressione 'this.x + 42' viene valutata in fase di esecuzione (a causa di 'questo' parola chiave), quando x è già inizializzato e ha valore 0.

Problemi correlati