Comprensión de esta advertencia: la clase Serializable no declara una serie de serialsionUid final estática

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

Pregunta

Tengo un código inicializador estático:

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

Por alguna razón, estoy recibiendo una advertencia de Eclipse: la clase Serializable no declara un final de serie estático.

¿Esto se queja de la clase anónima? ¿Qué puedo hacer al respecto, o debería suprimirlo?

¿Fue útil?

Solución

La sintaxis que estás usando se llama Inicialización de doble abrazadera - que en realidad es un "bloque de inicialización de instancia eso es parte de un clase interior anónima"(Ciertamente no es un truco). Entonces, cuando se usa esta notación, en realidad está definiendo una nueva clase (!).

El "problema" en su caso es que HashMap implementos Serializable. Esta interfaz no tiene ningún método y solo sirve para identificar la semántica de ser serializable. En otras palabras, es una interfaz marcadora y no tiene que implementar nada. Pero, durante la deserialización, Java usa un número de versión llamado serialVersionUID Para verificar que la versión serializada sea compatible con el objetivo. Si no proporciona esto serialVersionUID, se calculará. Y, como se documenta en el Javadoc de Serializable, el valor calculado es extremadamente sensible y, por lo tanto, se recomienda declarar explícitamente para evitar problemas de deserialización. Y esto es de lo que Eclipse se "queja" (tenga en cuenta que esto es solo una advertencia).

Entonces, para evitar esta advertencia, podrías agregar un serialVersionUID A su clase interior anónima:

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

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

Pero pierde la concisión de la sintaxis (y es posible que ni siquiera la necesite).

Por lo tanto, otra opción sería ignorar la advertencia agregando un @SuppressWarnings("serial") al método al que estás llamando someMethodThatTakesAHashMap(Map). Esto parece más apropiado en su caso.

Dicho todo esto, si bien esta sintaxis es concisa, tiene algunos inconvenientes. Primero, si mantiene una referencia en el objeto inicializado utilizando una inicialización de doble brecha, implícitamente mantiene una referencia al objeto exterior que no será elegible para la recolección de basura. Así que ten cuidado. Segundo (aunque suena como micro optimización), la inicialización de dos bracos tiene un muy un poco de sobrecarga. Tercero, esta técnica en realidad usa clases internas anónimas como vimos y, por lo tanto, come un poco de espacio de Permgen (pero dudo que esto sea realmente un problema a menos que usted De Verdad abusar de ellos). Finalmente, y este es quizás el punto más importante, no estoy seguro de que haga que el código sea más legible (no es una sintaxis bien conocida).

Entonces, aunque me gusta usarlo en pruebas (para la concisión), tiendo a evitar usarlo en código "regular".

Otros consejos

Sí, podrías suprimir la advertencia, pero la reescribiría así:

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

No se necesita suprimir, y mucho mejor leer, en mi opinión.

Generalmente estoy de acuerdo con Bart K., pero con fines informativos:
La advertencia también se puede eliminar agregando el campo, que se puede generar automáticamente al presionar CTRL+1.
La advertencia también se puede suprimir agregando la anotación @suppleswarnings ("serial") antes de la definición.
La clase anónima implementa serializable, y serializable requiere este campo estático para que las versiones puedan distinguirse al serializar y des-serializar. Más información aquí:
http://www.javablogging.com/what-is-serialversionUid/

los ImmutableMap La clase de la Biblioteca de Google Collections es útil para esta situación. p.ej

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

o

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

Para abordar la otra mitad de su pregunta, "¿Debo suprimirla?" -

Sí. En mi opinión, esta es una advertencia terrible. SerialVersionUid debe, por defecto, no ser utilizado, no al revés.

Si no agrega serie VersionUid, lo peor que sucede es que dos versiones de un objeto que en realidad son compatibles con serialización se consideran incompatibles. SerialVersionUid es una forma de declarar que la compatibilidad de la serialización no ha cambiado, anulando la evaluación predeterminada de Java.

Usando SerialVersionUid, lo peor que sucede es que inadvertidamente no actualiza la ID cuando la forma serializada de la clase cambia de manera incompatible. En el mejor de los casos, también recibe un error de tiempo de ejecución. En el peor de los casos, sucede algo peor. E imagine lo fácil que es dejar de actualizarlo.

Su intención era inicializar una instancia anónima de hashmap. La advertencia es la pista de que su código está haciendo más de lo que pretendía.

Lo que estamos buscando es una forma de inicializar una instancia anónima de hashmap. Lo que tenemos arriba crea una subclase anónima de hashmap y luego crea una instancia anónima de esa clase anónima.

Debido a que el código hace más de lo que se pretendía, lo llamaría un truco.

Lo que realmente queremos es algo como esto:

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

Pero, por desgracia, esto no es Java válido. No hay una manera de hacer nada de esto de manera segura usando una matriz de pares de clave/valor. Java Simple no tiene el poder expresivo.

El ImmutableMap de la colección de Google de la colección de Google está cerca, pero significa crear una versión del método de fábrica para varios números de pares de clave/valor. (Ver la respuesta de Finnw).

Así que mantén las cosas simples. Vaya con la solución de Bart K a menos que su código esté lleno de esta inicialización. Si es así, use ImmutableMap. O enrolle su propia subclase de hashmap con los métodos de fábrica de estilo "de". O cree estos métodos de fábrica de estilo "de" en una clase de utilidad. Aquí hay uno para dos pares de clave/valor:

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

Abrace la verbosidad y consuele en el conocimiento que sus compañeros de trabajo corporativos llevan los mismos grilletes que usted.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top