2012-07-31 18 views
9

Ho appena incontrato un problema interessante relativo alla serializzazione Java.Serializzare le mappe che sono inizializzate nei costruttori

Sembra che se la mia mappa è definita in questo modo:

Map<String, String> params = new HashMap<String, String>() {{ 
    put("param1", "value1"); 
    put("param2", "value2"); 
}}; 

e cerco di serializzare in un file con ObjectOutputStream:

ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(outputFile)); 
oos.writeObject(params); 

... ho java.io. NotSerializableException.

Tuttavia, se invece ho messo i valori alla mappa il modo standard:

Map<String, String> params = new HashMap<String, String>(); 
params.put("param1", "value1"); 
params.put("param2", "value2"); 

... allora il lavoro serializzazione bene.

Qualcuno può dirmi perché succede e qual è la differenza tra queste affermazioni? Penso che dovrebbero funzionare allo stesso modo, ma a quanto pare mi manca qualcosa.

risposta

10

Il primo esempio è la creazione di una classe interna anonima. Come ?

Map<String, String> params = new HashMap<String, String>() {}; 

creerebbe una nuova classe derivata da HashMap (notare le seguenti parentesi, in cui è possibile inserire i metodi, i membri ecc)

La mappa inizializzazione poi dichiara un blocco initialiser così:

Map<String, String> params = new HashMap<String, String>() { 
                  { // here } 
                  }; 

e in ciò chiamate i metodi della popolazione.

Questo idioma va bene, ma è necessario essere consapevoli del fatto che si sta creando una nuova classe, non solo un nuovo oggetto.

Poiché questa classe è una classe interna, avrà un puntatore implicito this alla classe esterna contenente. La tua classe anonima sarebbe serializzabile a causa della sua derivazione da una classe serializzabile. Tuttavia la tua classe esterna (a cui fa riferimento il puntatore this) non lo è.

Strumenti come XStream, che eseguono la serializzazione in XML tramite la riflessione, scopriranno il puntatore this e tenteranno di serializzare l'oggetto circostante, il che è altrettanto confuso.

+1

Per "inizializzatore statico", si intende "inizializzatore di istanza"? –

+0

quindi quale sarebbe la classe di chiusura prevista? – Shark

+0

@ Eng.Fouad - whoops. Modificato –

0

ho voluto integrare la risposta di @ Brian Agnew con questo suggerimento:

ho avuto un caso in cui avevo bisogno di un comportamento leggermente diverso da un oggetto, così ho esteso le sue capacità con una classe interna anonima come avete fatto nel esempio. La classe esterna era un'applicazione GUI e non l'ho serializzabile perché non era necessaria, quindi, come ha detto @Brian, nessuna classe interna anonima potrebbe essere serializzabile, anche se le classi che stavano estendendo erano.

In questa situazione, è sufficiente definire un comportamento diverso per quando una classe viene deserializzata e quando viene nuovamente serializzata.Se si dispone di una classe con un costruttore specifico, utilizzare un metodo come questo nella vostra classe:

public FunctionalObject getNewFunctionalObject (String param1, String param2) { 
    // Use an anonymous inner class to extend the behavior 
    return new FunctionalObject (param1, param2) { 
     { 
      // Initialization block code here 
     } 
     // Extended behavior goes here 
    }; 
} 

Così, quando si deserializzazione, è possibile effettuare una chiamata in questo modo:

FunctionalObject fo = (FunctionalObject) objectInputStream.readObject(); 
fo = getNewFunctionalObject(fo.getParam1(), fo.getParam2()); 

durante la serializzazione, sarà necessario creare un oggetto new che sia un clone del vecchio oggetto. Alcune classi hanno questo comportamento integrato, mentre in altre dovrai definirlo in modo specifico. Per la serializzazione, se si dispone di un costruttore che può clonare, o se la classe ha il metodo clone definito, si potrebbe fare questo:

objectOutputStream.writeObject (fo.clone()); 

Poi, la clone di tale oggetto non sarà più un punto di riferimento per la vostra classe interna anonima, ma un riferimento a una copia effettiva dell'oggetto, che è serializzabile.

Nel caso del tuo esempio, si potrebbe avere appena fatto questo:

// Assuming objectOutputStream has already been defined 
Map<String, String> params = new HashMap<String, String>() {{ 
    put("param1", "value1"); 
    put("param2", "value2"); 
}}; 
objectOutputStream.writeObject (new HashMap<String,String> (params)); 

Questo funziona perché la classe HashMap ha un costruttore che restituirà un clone di qualsiasi HashMap è passato in esso. Era un sacco di parole per dire qualcosa di semplice, ma avrei desiderato che avrei avuto questo consiglio prima di me stesso.

Problemi correlati