2009-11-02 12 views
13

Tengo una clase que es para uso inmutable, por lo tanto, me gustaría etiquetar todos los campos final.Serialización y objetos inmutables

Sin embargo, la clase se serializa y se deserializa para enviar a través de la red. Para que esto funcione, se requiere un constructor vacío. Esto me impide crear los campos finales.

Estoy seguro de que este es un problema bastante común, pero no puedo encontrar una solución. ¿Cómo debo proceder?

Respuesta

7

En el caso típico de serialización, no es necesario que la clase tenga un constructor vacío o campos que no sean finales para ser serializables.

Ahora, si tiene que hacer su propia serialización, o necesita subclase una clase que no implementa Serializable, esa es una historia diferente.

Por lo tanto, debe proporcionar más detalles sobre cómo está teniendo un problema.

+1

Gracias, yo estaba usando el método típico de serialización, pero siempre habían suministrado un constructor vacío ya que era la forma en que pensé que trabajó. – Pool

6

No se requiere un constructor no-arg. La clase más derivada no serializable necesita un constructor no-arg disponible para la clase serializable menos derivada.

Si necesita mutar campos dentro de un readObject, utilice un proxy serie a través de readResolve y writeReplace.

5

Este problema es un open bug on the Java language. (Tenga en cuenta que esto solo se aplica si tiene que hacer la serialización manualmente, como con readObject)

+0

De la evaluación: "el problema se aplica a los campos de instancia final que no sean los campos serializables de la clase", por lo que en el caso estándar funciona bien. Nick parece estar haciendo algo diferente. – Yishai

+0

Ah, sí, debería agregar un descargo de responsabilidad que esto solo aplica si tiene que enganchar en readObject o algo así. –

3

Para repetir lo que se ha dicho, los constructores no-arg no son un requisito si está tomando la ruta de implementación de la interfaz java.io.Serializable . Eche un vistazo al código fuente java.lang.Integer, por ejemplo, una clase simple serializable/inmutable que tiene dos constructores: uno que toma un int, y uno que toma un String. Código fuente: http://www.docjar.com/html/api/java/lang/Integer.java.html. Javadoc: http://java.sun.com/javase/6/docs/api/java/lang/Integer.html.

Además, dependiendo de la complejidad de su clase y de lo que esté haciendo, podría considerar implementar la serialización a través de la interfaz java.io.Externalizable (aunque algunos la consideran obsoleta, y NECESITA un constructor no arg). Aquí hay una descripción general de SO: What is the difference between Serializable and Externalizable in Java?, y aquí está el tutorial oficial de Java: http://java.sun.com/docs/books/tutorial/javabeans/persistence/index.html.

3

Para el registro, ya que tenía un problema similar:
que tenía un mensaje "java.io.InvalidClassException: com.example.stuff.FooBar; com.example.stuff.FooBar; ningún constructor válida"

Pensé que era porque faltaba un constructor predeterminado. Pero las respuestas anteriores confirman que no es obligatorio (pero nuestra aplicación utiliza un antiguo serializador que de hecho requiere un constructor predeterminado, por lo que puede surgir el caso).

Entonces me encontré con una página que dice:

Si una clase que está diseñado para la herencia no es serializable, se puede ser imposible escribir una subclase serializable. Específicamente, será imposible si la superclase no proporciona un constructor sin parámetros accesible.

De ahí el mensaje que obtuve, supongo.Parecía que el problema central era clásico: ¡Declaré que una clase era serializable, pero la superclase no lo era! Moví la interfaz Serializable a la jerarquía, y todo estuvo bien.

Pero el mensaje fue un poco engañoso ... :-)

0

No es necesario un constructor sin argumentos. Vamos a leer el código fuente:

// java.io.ObjectStreamClass 
private static Constructor<?> getSerializableConstructor(Class<?> cl) { 
    Class<?> initCl = cl; 
    while (Serializable.class.isAssignableFrom(initCl)) { 
     if ((initCl = initCl.getSuperclass()) == null) { 
      return null; 
     } 
    } 
    ... 
} 

Por lo tanto, en realidad se requiere el constructor sin argumentos en el más cercano no Serializable clase en la jerarquía de tipos.

Significa que la siguiente clase Domain se puede serializar.

class Domain implements Serializable { 
    private final int a; 

    public Domain(int a) { 
     this.a = a; 
    } 
} 

Pero la clase Son no puede:

class Father{ 
    private final int a; 

    public Father(int a) { 
    this.a = a; 
    } 
} 

class Son extends Father implements Serializable { 
    public Son(int a) { 
    super(a); 
    } 
} 
Cuestiones relacionadas