Comprendre cet avertissement: la classe sérialisable ne déclare pas un sérialversionuide final statique

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

Question

J'ai du code d'initialiseur statique:

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

Pour une raison quelconque, je reçois un avertissement d'Eclipse: la classe sérialisable ne déclare pas un SerialVersionUID final statique.

Est-ce que cela se plaint de la classe anonyme? Que puis-je faire à ce sujet, ou devrais-je le supprimer.

Était-ce utile?

La solution

La syntaxe que vous utilisez est appelée Initialisation à double bracelet - qui est en fait un "Bloque d'initialisation d'instance Cela fait partie d'un classe intérieure anonyme"(Certainement pas un hack). Donc, lorsque vous utilisez cette notation, vous définissez réellement une nouvelle classe (!).

Le "problème" dans votre cas est que HashMap met en oeuvre Serializable. Cette interface n'a aucune méthode et ne sert qu'à identifier la sémantique d'être sérialisable. En d'autres termes, c'est une interface de marqueur et vous n'avez rien à mettre en œuvre. Mais, pendant la désérialisation, Java utilise un numéro de version appelé un serialVersionUID Pour vérifier que la version sérialisée est compatible avec la cible. Si vous ne fournissez pas ceci serialVersionUID, il sera calculé. Et, comme documenté dans le javadoc de Serializable, la valeur calculée est extrêmement sensible et il est donc recommandé de le déclarer explicitement pour éviter tout problème de désérialisation. Et c'est ce dont Eclipse "se plaindre" (notez que ce n'est qu'un avertissement).

Donc, pour éviter cet avertissement, vous pouvez ajouter un serialVersionUID à votre classe intérieure anonyme:

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

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

Mais vous perdez la concision de la syntaxe (et vous n'en avez peut-être même pas besoin).

Une autre option serait donc d'ignorer l'avertissement en ajoutant un @SuppressWarnings("serial") à la méthode où vous appelez someMethodThatTakesAHashMap(Map). Cela semble plus approprié dans votre cas.

Le fait que tout soit dit, bien que cette syntaxe soit concise, elle présente certains inconvénients. Tout d'abord, si vous tenez une référence sur l'objet initialisé à l'aide d'une initialisation à double bracelet, vous maintenez implicitement une référence à l'objet extérieur qui ne sera pas éligible à la collecte des ordures. Donc sois prudent. Deuxièmement (cela ressemble à une micro-optimisation), l'initialisation à double bracelet a un très un peu de frais généraux. Troisièmement, cette technique utilise en fait des classes intérieures anonymes comme nous l'avons vu et mange ainsi un peu d'espace Permgen (mais je doute que ce soit vraiment un problème à moins que vous vraiment les abuser). Enfin - et c'est peut-être le point le plus important - je ne suis pas sûr que cela rend le code plus lisible (ce n'est pas une syntaxe bien connue).

Donc, même si j'aime l'utiliser dans les tests (pour la concision), j'ai tendance à éviter de l'utiliser dans le code "régulier".

Autres conseils

Oui, vous pourriez supprimer l'avertissement, mais je le réécrivais comme ceci:

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

Aucune suppression nécessaire, et beaucoup mieux à lire, OMI.

Je suis généralement d'accord avec Bart K., mais à des fins d'information:
L'avertissement peut également être éliminé en ajoutant le champ, qui peut être généré automatiquement en frappant Ctrl + 1.
L'avertissement peut également être supprimé en ajoutant l'annotation @SuppressWarnings ("Serial") avant la définition.
La classe anonyme implémente sérialisable et sérialisable nécessite ce champ statique afin que les versions puissent être distinguées lors du sérialisation et du désérialisation. Plus d'informations ici:
http://www.javablogging.com/what-is-serialversionuid/

La ImmutableMap La classe de la bibliothèque Google Collections est utile pour cette situation. par exemple

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

ou

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

Pour répondre à l'autre moitié de votre question, "Dois-je le supprimer?" -

Oui. À mon avis, c'est un terrible avertissement. SerialVersionUid devrait par défaut ne pas être utilisé, pas l'inverse.

Si vous n'ajoutez pas SerialVersionUID, la pire chose qui se produit est que deux versions d'un objet compatibles en sérialisation sont jugées incompatibles. SerialVersionUid est un moyen de déclarer que la compatibilité de sérialisation n'a pas changé, dépassant l'évaluation par défaut de Java.

En utilisant SerialVersionUID, la pire chose qui se produit est que vous ne parviens pas par inadvertance à mettre à jour l'ID lorsque le formulaire sérialisé de la classe change de manière incompatible. Au mieux, vous obtenez également une erreur d'exécution. Au pire, quelque chose de pire se produit. Et imaginez à quel point il est facile de ne pas le mettre à jour.

Votre intention était d'initialiser une instance anonyme de hashmap. L'avertissement est l'indice que votre code fait plus que ce que vous ne l'aviez prévu.

Ce que nous recherchons, c'est un moyen d'initialiser une instance HashMap anonyme. Ce que nous avons ci-dessus crée une sous-classe anonyme de hashmap crée alors une instance anonyme de cette classe anonyme.

Parce que le code fait plus que ce qui était prévu, je l'appellerais un hack.

Ce que nous voulons vraiment, c'est quelque chose comme ça:

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

Mais hélas, ce n'est pas un java valide. Il n'y a pas de moyen de faire quoi que ce soit de manière à utiliser un tableau de paires de clés / valeur. Java Simple n'a pas le pouvoir expressif.

Les méthodes ImmutableMap de Google Collection sont proches, mais cela signifie créer une version de la méthode d'usine pour divers nombres de paires de clés / valeur. (Voir la réponse de Finnw.)

Alors gardez les choses simples. Allez avec la solution de BART K à moins que votre code ne soit jonché de cette initialisation. Si c'est le cas, utilisez IMMUTABLEMAP. Ou lancez votre propre sous-classe Hashmap avec les méthodes d'usine de style "de". Ou créez ces méthodes d'usine "de style" dans une classe d'utilité. En voici un pour deux paires de clés / valeur:

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;
    }
}

Embrassez la verbosité et prenez du réconfort dans les connaissances que vos collègues d'entreprise portent les mêmes chaînes que vous.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top