2009-04-27 11 views
5

Ho una classe Set (questo è J2ME, quindi ho accesso limitato all'API standard, solo per spiegare la mia apparente reinvenzione delle ruote). Sto usando la mia classe set per creare serie costanti di cose in classi e sottoclassi. E 'sorta di simile a questo ...Posso garantire l'ordine in cui vengono eseguiti gli inizializzatori statici in Java?

class ParentClass 
{ 
    protected final static Set THE_SET = new Set() {{ 
     add("one"); 
     add("two"); 
     add("three"); 
    }}; 
} 


class SubClass extends ParentClass 
{ 
    protected final static Set THE_SET = new Set() {{ 
     add("four"); 
     add("five"); 
     add("six"); 
     union(ParentClass.THE_SET); /* [1] */ 
    }}; 
} 

Tutto sembra bene, tranne la linea a [1] causa un'eccezione puntatore nullo. Presumibilmente ciò significa che l'inizializzatore statico nella sottoclasse viene eseguito prima di quello della classe genitore. Questo mi ha sorpreso perché pensavo che avrebbe eseguito prima i blocchi statici in qualsiasi nuova importazione, prima di eseguire qualsiasi nella sottoclasse istatiated.

Ho ragione in questa ipotesi? C'è un modo per controllare o aggirare questo comportamento?

Aggiornamento:

Le cose sono ancora più strano. Ho provato questo posto (si noti la 'nuova ParentClass()' line):

class ParentClass 
{ 
    public ParentClass() 
    { 
     System.out.println(THE_SET); 
    } 

    protected final static Set THE_SET = new Set() {{ 
     add("one"); 
     add("two"); 
     add("three"); 
    }}; 
} 


class SubClass extends ParentClass 
{ 
    protected final static Set THE_SET = new Set() {{ 
     System.out.println("a"); 
     new ParentClass(); 
     System.out.println("b"); 
     add("four"); 
     System.out.println("c"); 
     add("five"); 
     System.out.println("d"); 
     add("six"); 
     System.out.println("e"); 
     union(ParentClass.THE_SET); /* [1] */ 
     System.out.println("f"); 
    }}; 
} 

e l'uscita è strana:

a 
["one", "two", "three"] 
b 
c 
d 
e 
Exception in thread "main" java.lang.ExceptionInInitializerError 
Caused by: java.lang.NullPointerException 

Così ParentClass viene inizializzato, ma la sottoclasse non ha accesso a nel suo inizializzatore statico.

risposta

7

È questo che stai cercando di realizzare? O hai bisogno di un'implementazione locale dell'interfaccia Set?

class ParentClass 
{ 
    protected final static Set THE_SET; 

    static { 
     THE_SET = new HashSet(); 
     THE_SET.add("one"); 
     THE_SET.add("two"); 
     THE_SET.add("three"); 
    } 
} 


class SubClass extends ParentClass 
{ 
    protected final static Set THE_SECOND_SET; 

    static { 
     THE_SECOND_SET = new HashSet(); 
     THE_SECOND_SET.add("four"); 
     THE_SECOND_SET.add("five"); 
     THE_SECOND_SET.add("six"); 
     union(ParentClass.THE_SET); /* [1] */ 
    } 
} 
+0

Sì ... fa la stessa cosa. – izb

+1

Penso che Elijah abbia ragione. Questo non è un problema di ordine di inizializzazione, ma piuttosto una sorta di scontro di nomi. – boutta

+0

Nel mio codice reale, ho effettivamente 3 classi dove A <-B <-C. L'eccezione era in B. Interessantemente dare a ogni classe il proprio nome di set sposta l'eccezione a C ma non risolve il problema. Ciò implica semplicemente che l'ordine è davvero imprevedibile. – izb

3

Non esiste alcuna garanzia per l'ordine di inizializzazione statico tra le classi. All'interno di una classe, vengono eseguiti nell'ordine del codice sorgente.

Se ci pensate, non può davvero essere un ordine tra le classi, perché non controllate quando vengono caricate le classi; potresti caricare dinamicamente una classe, oppure la JVM potrebbe ottimizzare l'ordine di caricamento.

+0

Se la classe A estende la classe B, la classe A non deve essere completamente caricata prima che la classe B possa essere caricata? –

+0

Avrei pensato che sarebbe stato il contrario. Sicuramente A dipende da B, quindi B dovrebbe essere inizializzato per primo. – izb

+0

Ma per ottenere (o sapere di B) deve prima passare attraverso il codice A. Significa che un codice di blocco statico accadrà prima del codice in B, penserei. –

2

Anche se non si dispone del extends ParentClass, utilizzare ParentClass dovrebbe causare l'inizializzazione.

Dove le cose diventano difficili quando si hanno cicli. Con un ciclo è possibile accedere a una classe prima che sia stata completamente inizializzata. Poiché Java è un sistema multithread, puoi anche avere problemi con deadlock e gare.

Può essere che l'implementazione di Java ME sia bacata (non inaudita). Parte il tuo completo ParentClass riferimenti il ​​tuo ChildClass. O forse c'è qualche altro bug di applicazione/libreria.

Su una nota correlata, se non si specifica -target 1.4 o successiva, la classe interna non viene inizializzata non appena si prevede. Come presentato il tuo codice usa le classi interne (tecnicamente) in un contesto statico, quindi non dovrebbe essere un problema.

Inoltre, è interessante sottolineare che l'inizializzazione statica è leggermente confusa in situazioni come questa, perché in realtà ci sono quattro classi lì.

0

Data la linea di uscita [ "uno", "due", "tre"], è praticamente impossibile che ParentClass.THE_SET è mai stato inizializzato.

Naturalmente, è possibile che non sia coinvolto solo un classloader, ma sarebbe certamente utile vedere il metodo e il numero di riga in cui avviene il puntatore nullo.

+0

J2ME non consente i classloader personalizzati .. è piuttosto primitivo. Inoltre, la mancanza di "f" sull'output implica che l'errore si sia verificato sulla riga union() che lo precede. – izb

2

Basta smettere di abusare del concetto di classi anonime per l'inizializzazione dell'istanza (il cosiddetto "doppio bretone").

+0

Sai che ho sempre pensato che fosse solo una sintassi comoda. Non avevo idea che fosse in realtà una classe anonima. – izb

+0

Questa è la ragione principale per cui odio le persone definendole un "idioma" e dandole un nome - lo fa sembrare una caratteristica separata del linguaggio e oscura ciò che accade realmente. –

-1

Penso che qualcosa di simile sia stato scritto nel libro dei rompicapo di Java o nel video YouTube di google sui trucchi java.

Problemi correlati