Comprendere questo avvertimento: la classe serializzabile non dichiara un serialversionuid statico finale

StackOverflow https://stackoverflow.com/questions/1514881

Domanda

Ho del codice iniziale statico:

someMethodThatTakesAHashMap(new HashMap<K, V>() {
{
  put("a","value-a"); 
  put("c","value-c");}
});

Per qualche motivo ricevo un avvertimento dall'eclissi: la classe serializzabile non dichiara un serialversionuid statico.

Si lamenta della classe anonima? Cosa posso fare al riguardo, o dovrei solo sopprimere.

È stato utile?

Soluzione

La sintassi che stai usando si chiama Inizializzazione a doppia braccia - Che in realtà è un "blocco di inizializzazione dell'istanza Questo fa parte di un Classe interna anonima"(Certamente non un hack). Quindi, quando usi questa notazione, stai effettivamente definendo una nuova classe (!).

Il "problema" nel tuo caso è quello HashMap strumenti Serializable. Questa interfaccia non ha alcun metodo e serve solo per identificare la semantica dell'essere serializzabili. In altre parole, è un'interfaccia marker e non devi implementare concretamente nulla. Ma, durante la deserializzazione, Java utilizza un numero di versione chiamato a serialVersionUID Per verificare che la versione serializzata sia compatibile con il target. Se non lo fornisci serialVersionUID, verrà calcolato. E, come documentato nel Javadoc di Serializable, il valore calcolato è estremamente sensibile e si raccomanda quindi di dichiararlo esplicitamente per evitare eventuali problemi di deserializzazione. E questo è ciò di cui l'eclissi sta "lamentando" (nota che questo è solo un avvertimento).

Quindi, per evitare questo avvertimento, potresti aggiungere un serialVersionUID Alla tua classe interiore Annonymous:

someMethodThatTakesAHashMap(new HashMap<String, String>() {
    private static final long serialVersionUID = -1113582265865921787L;

    {
        put("a", "value-a");
        put("c", "value-c");
    }
});

Ma perdi la concisione della sintassi (e potresti non averne nemmeno bisogno).

Un'altra opzione sarebbe quindi quella di ignorare l'avvertimento aggiungendo un @SuppressWarnings("serial") al metodo in cui stai chiamando someMethodThatTakesAHashMap(Map). Questo sembra più appropriato nel tuo caso.

Detto questo, mentre questa sintassi è concisa, ha alcuni svantaggi. Innanzitutto, se si tiene un riferimento sull'oggetto inizializzato usando un'inizializzazione a doppia brace, si tiene implicitamente un riferimento all'oggetto esterno che non sarà idoneo per la raccolta della spazzatura. Quindi sii attento. Secondo (sembra però una micro ottimizzazione), l'inizializzazione a doppia braccia ha a Molto un po 'di sovraccarico. In terzo luogo, questa tecnica utilizza effettivamente le classi interne anonime come abbiamo visto e quindi mangia un po 'di spazio di ammissione (ma dubito che questo sia davvero un problema a meno che tu veramente abusarli). Infine - e questo è forse il punto più importante - non sono sicuro che renda il codice più leggibile (non è una sintassi ben nota).

Quindi, mentre mi piace usarlo nei test (per la concisione), tendo a evitare di usarlo in codice "regolare".

Altri suggerimenti

Sì, potresti sopprimere l'avvertimento, ma lo riscriverei in questo modo:

HashMap<String, String> map  = new HashMap<String, String>();
map.put("a","value-a"); 
map.put("c","value-c");
someMethodThatTakesAHashMap(map);

Nessuna soppressione necessaria e molto meglio da leggere, IMO.

In genere sono d'accordo con Bart K., ma per scopi informativi:
L'avvertimento può anche essere eliminato aggiungendo il campo, che può essere generato automaticamente colpendo CTRL+1.
L'avvertimento può anche essere soppresso aggiungendo l'annotazione @SuppressWarnings ("seriale") prima della definizione.
La classe anonima implementa serializzabile e serializzabile richiede questo campo statico in modo che le versioni possano essere distinte durante la serializzazione e la de-serializzazione. Maggiori informazioni qui:
http://www.javablogging.com/what-is-serialversionuid/

Il ImmutableMap La lezione della libreria di Google Collections è utile per questa situazione. per esempio

someMethodThatTakesAHashMap(ImmutableMap.<K, V>builder().put("a","value-a").put("c","value-c").build());

o

someMethodThatTakesAHashMap(ImmutableMap.of("a","value-a","c","value-c"));

Per affrontare l'altra metà della tua domanda "Dovrei sopprimerla?" -

Sì. Secondo me, questo è un terribile avvertimento. serialversionuid dovrebbe per impostazione predefinita non essere usato, non viceversa.

Se non si aggiungono serialversionuid, la cosa peggiore che accade è che due versioni di un oggetto che sono effettivamente compatibili con serializzazione sono considerate incompatibili. Il serialversionuid è un modo per dichiarare che la compatibilità di serializzazione non è cambiata, prevalente sulla valutazione predefinita di Java.

Usando serialversionuid, la cosa peggiore che accade è che inavvertitamente non riesce ad aggiornare l'ID quando la forma serializzata della classe cambia in modo incompatibile. Nella migliore delle ipotesi, ricevi anche un errore di runtime. Nel peggiore dei casi, succede qualcosa di peggio. E immagina quanto sia facile non aggiornarlo.

Il tuo intento era di inizializzare un'istanza anonima di hashmap. L'avvertimento è indizio che il tuo codice sta facendo più del previsto.

Quello che stiamo cercando è un modo per inizializzare un'istanza hashmap anonima. Ciò che abbiamo sopra crea una sottoclasse anonima di hashmap, quindi crea un'istanza anonima di quella classe anonima.

Poiché il codice fa più del previsto, lo definirei un hack.

Quello che vogliamo veramente è qualcosa del genere:

foo(new HashMap<String, String>({"a", "value-a"}, {"c", "value-c"}));

Ma ahimè questo non è valido Java. Non c'è un modo per fare nulla in modo questo in modo sicuro usando una matrice di coppie chiave/valore. Java Simple non ha il potere espressivo.

I immutablemap.f statica della collezione di Google sono vicini, ma significa creare una versione del metodo di fabbrica per vari numeri di coppie chiave/valore. (Vedi la risposta di Finnw.)

Quindi mantieni le cose semplici. Vai con la soluzione di Bart K a meno che il codice non sia disseminato di questa inizializzazione. In tal caso, usa immutablemap. Oppure rotola la tua sottoclasse hashmap con i metodi di fabbrica "di" in stile. Oppure crea questi metodi di fabbrica "di" di stile in una classe di utilità. Eccone uno per due coppie chiave/valore:

public final MapUtil {
    public static <K,V> Map<K,V> makeMap(K k1, V v1, K k2, V v2) {
        Map<K,V> m = new HashMap<K,V>();
        m.put(k1, v1);
        m.put(k2, v2);
        return m;
    }
}

Abbraccia la verbosità e prendi conforto nella consapevolezza che i tuoi colleghi aziendali indossano le stesse catene di te.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top