miércoles, 20 de julio de 2011

A vueltas con el serialVersionUID

Tengo un compañero de trabajo que últimamente ha sufrido mucho con este tema por culpa del Eclipse y del famoso warning The serializable class Objeto does not declare a static final serialVersionUID field of type long. ¿Por qué? El resolver automáticamente de Eclipse no siempre es lo más adecuado.

Este warning aparece cuando tenemos una clase que es Serializable pero no define el campo. El mecanismo de Serialización de Java sirve para coger un objeto y guardarlo como un chorro de bytes. Y ese chorro de bytes puede ser enviado por red, o guardado en disco.

El principal problema de la serialización es la deserialización. Puede que la aplicación que deserializa el objeto no se esté ejecutando en la misma versión de la máquina virtual. O que la clase cargada en la máquina virtual deserializadora tenga una versión diferente de la clase. En esos casos la deserialización puede fallar. Y el mecanismo que ofrece Java para controlarlo es el serialVersionUID.

Cuando no se define, es el compilador el que genera un valor aleatorio para el atributo. Así, diferentes compilaciones tendrán distintos valores, y al intentar deserializar un objeto serializado con otra versión del código obtendremos un
java.io.InvalidClassException: Objeto; local class incompatible: stream classdesc
serialVersionUID = 1, local class serialVersionUID = 2
Por el contrario, si dejamos un valor generado por el Eclipse el mecanismo de deserialización nunca detectará que los objetos son diferentes. Y puede que, en muchos casos, eso no sea lo que queremos.

La posibilidad de definir el serialVersionUID abre la posibilidad a que sea el desarrollador el que gestione cuándo las diferentes versiones de las clases son compatibles en serialización y cuándo no. Si la nueva versión de la clase no es compatible (por ejemplo porque se han añadido nuevos atributos a la clase) debería cambiarse el valor.

¿Cual es mi recomendación? Mi recomendación es, SIEMPRE, ser precavido. Así que, ante la duda, dejar sin definir el serialVersionUID . Y si los warnings molestan, ponerles un @SuppressWarnings("serial") . Y dejar la definición del serialVersionUID sólo para cuando sea necesario mantener compatibilidad entre versiones de clases.

7 comentarios:

  1. Como tengo falta de experiencia no se me ocurre un caso en el que no sea necesario el serialVersionUID. ¿Puedes ponerme un ejemplo en el que no sea necesario mantener la compatibilidad entre versiones de clases? No me imagino deserializar algo que no encaje remotamente sin correr riesgos... :S

    ResponderEliminar
  2. Yo creo que es, más bien, todo lo contrario. El almacenamiento de datos 'a largo plazo' no se suele hacer por medio de serialización por problemas obvios de compatibilidad entre versiones ¿cómo compatibilizas el añadir un atributo a una clase? En almacenamientos a largo plazo se suelen guardar los datos en una BBDD o serializar a texto plano, XML, JSON, ...

    La serialización de la JVM se suele emplear, al menos en entorno web, para meter atributos en la sesión http; o en GWT, para poder garantizar la traducción de beans a JSON , ... Lo raro es persistir un objeto en disco.

    ResponderEliminar
  3. Donde más se usa es:
    - Caches. Ejemplo: EHCache y JBossCache, cuando ocupa demasiado en memoria (a partir de cierto límite) se serializa.

    - BBDD Orientadas a objeto

    - En sentido estricto: Guardar en sesión no requiere serializacion, salvo que sea un cluster o persistent sessions. Así, HttpSession.setAttribute() no requiere Serializable, aunque es habitual que pete cuando guardas algo que no lo es.

    ResponderEliminar
  4. Perdón, el tercer uso es buses de mensajería: JMS o propietarios como Tibco.

    ResponderEliminar
  5. Ya sabes que esas son cosas de mayores y yo no me meto donde no sé.

    ResponderEliminar
  6. Me encantó su blog hacen muy buenos artículos, ojala fueran mas hehe , saludos desde México

    ResponderEliminar
  7. Gracias, intentamos hacer lo que podemos con el blog, pero últimamente el trabajo aprieta y ahoga :D . Esperamos hacer un par de artículos antes de irnos de vacaciones.

    Un saludo!

    ResponderEliminar