2013-01-18 7 views
15

So che questo codice:L'inizializzazione della doppia parentesi Java funziona sempre?

Set<String> set = new HashSet<String>() {{ 
    add("test1"); 
    add("test2"); 
}}; 

è davvero:

Set<String> set = new HashSet<String>() { 
    {//initializer 
    add("test1"); 
    add("test2"); 
    } 
}; 

Il blocco di inizializzazione viene eseguito prima il blocco di costruzione. Nell'esempio precedente, add ("test1") viene chiamato prima che il costruttore sia eseguito. Il costruttore potrebbe inizializzare molti dei campi di istanza, in modo che questa classe possa funzionare. Mi chiedo perché chiamare .add() prima che il costruttore funzioni? C'è qualche caso che causa un problema?

+2

Questo probabilmente rientra in "comportamento non specificato" ... – 11684

+2

Interessante domanda. Non ho una risposta, ma penso che stai facendo un'assunzione errata qui. Se si guarda il costruttore di HashSet, fa questo: 'map = new HashMap ();' e il metodo add fa questo: 'restituisce map.put (e, PRESENT) == null;'. Se la tua ipotesi era corretta, ciò causerebbe un NPE. –

+0

[Non è certamente sbagliato pensare se questo "modello" valga davvero la pena] (http://stackoverflow.com/q/924285/521799) –

risposta

17

C'è un dettaglio che hai tralasciato che lo spiega.

Prima di tutto, la recensione di lasciare passaggi da 3 a 5 del initialization procedure (riassunto):

3. la costruzione della superclasse è chiamato
4. gli inizializzatori di istanza sono chiamati
5. il corpo del costruttore è chiamato

Il dettaglio che hai tralasciato è che la tua espressione non sta semplicemente creando una nuova istanza della classe HashSet, in realtà sta creando una nuova istanza di una sottoclasse anonima di HashSet. (Credo che questo è specificato in section 15.9.1.)

Poiché non è stato dichiarato un costruttore, viene utilizzato il costruttore predefinito. Ma prima, il costruttore della superclasse HashSet è stato completato.

Quindi, in breve, il costruttore HashSet viene completato prima dell'esecuzione del blocco di inizializzazione.

+0

Grazie, Samuel. Hai spiegato esattamente il pezzo che mancava. Questo blocco di codice non sta inizializzando HashSet, ma una sottoclasse anonima di HashSet. – user926958

+0

@ user926958, è un dettaglio semplice da non perdere in questo esempio. Se tu fossi derivato da un'interfaccia come 'Set' o una classe astratta, ti sarebbe richiesto di aggiungere alcune definizioni di metodo che renderebbero più ovvia una nuova classe in fase di definizione. –

6

Questo presupposto è sbagliato:

Il blocco di inizializzazione viene eseguito prima del blocco di costruzione.

Poiché in questo caso particolare, il blocco di inizializzazione fa parte blocco di costruzione.

Il docs affermare con chiarezza che

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

Penso che tu stia confondendo con gli inizializzatori statici.

+0

Controlla il mio commento sulla domanda originale. Il blocco non dovrebbe verificarsi dopo ogni costruttore anziché prima di esso? Non che tu stia dicendo il contrario, voglio solo capire meglio. –

+0

L'ordine sembra essere inizializzatore prima del costruttore, almeno l'ho preso da qui: http://stackoverflow.com/questions/2007666/in-what-order-do-static-initializer-blocks-in-java-run – user926958

+1

Non intendo essere scortese ma NON è quello che sta succedendo; il blocco initalizzatore viene chiamato dopo super ma prima del resto del costruttore che causerebbe problemi nella maggior parte dei casi; sta facendo un'estensione in linea della classe HashMap ed è per questo che funziona sempre. –

4

Gli inizializzatori di istanza vengono eseguiti subito dopo la costruzione dell'oggetto. Fondamentalmente stai creando un'estensione in linea di HashSet e quindi "subito dopo" viene creata l'aggiunta di due elementi.

Questo è un modello di uso comune in oggetti fittizi per il test, come in JMock, ma ha anche altri usi pratici.

Spero che questo aiuti.

+2

Non viene eseguito dopo la costruzione dell'oggetto. Viene eseguito durante la costruzione dell'oggetto, tra il costruttore super-classe e il corpo del costruttore. –

3

Ritengo che sia una cattiva pratica perché crea sottoclassi inutili che possono influire sull'utilizzo della memoria e sulle prestazioni dell'applicazione. Ad ogni modo, il programma è corretto perché il costruttore della superclasse viene chiamato prima degli inizializzatori dell'istanza. Pertanto, quando viene eseguito l'inizializzatore, il costruttore HashSet è in esecuzione, pertanto la chiamata a add funzionerà.

Problemi correlati