Pregunta

El ObjectOutputStream.writeStreamHeader() método se puede anular a anteponer o añadir datos a la cabecera. Sin embargo, si esos datos se basa en un argumento pasado al constructor de la clase derivada como:

public class MyObjectOutputStream extends ObjectOutputStream {

    public MyObjectOutputStream( int myData, OutputStream out ) throws IOException {
        super( out );
        m_myData = myData;
    }

    protected void writeStreamHeader() throws IOException {
        write( m_myData );            // WRONG: m_myData not initialized yet
        super.writeStreamHeader();
    }

    private final int m_myData;
}

no funciona porque super() se llama antes de m_myData se inicializa y super() llama writeStreamHeader(). La única manera que puedo pensar para evitar esto es mediante el uso de ThreadLocal como:

public class MyObjectOutputStream extends ObjectOutputStream {

    public MyObjectOutputStream( int myData, OutputStream out ) throws IOException {
        super( thunk( myData, out ) );
    }

    protected void writeStreamHeader() throws IOException {
        write( m_myData.get().intValue() );
        super.writeStreamHeader();
    }

    private static OutputStream thunk( int myData, OutputStream out ) {
        m_myData.set( myData );
        return out;
    }

    private static final ThreadLocal<Integer> m_myData = new ThreadLocal<Integer>();
}

Esto parece funcionar, pero hay una manera mejor (menos torpe)?

¿Fue útil?

Solución

Hay una manera general para resolver este tipo de problema. Hacer la clase y clase interna y hacer referencia a una variable en el ámbito exterior. (Nota: esto sólo funciona con -target 1.4 o Greter, que es el valor por defecto en las versiones actuales de javac. Con -target 1.3 obtendrá una NPE).

public static ObjectOutputStream newInstance(
    final int myData, final OutputStream out
) throws IOException {
    return new ObjectOutputStream(out) {
        @Override
        protected void writeStreamHeader() throws IOException {
            write(myData);
            super.writeStreamHeader();
        }
    };
}

Sin embargo, es probablemente más fácil simplemente escriba los datos antes de construir la ObjectOuputStream.

Otros consejos

¿No podría hacerlo de esta manera. Ignorar la llamada writeStreamHeader desde el constructor super y hacer uno usted mismo, cuando se ha inicializado el campo necesario:

public class MyObjectOutputStream extends ObjectOutputStream {

private boolean initalized = false;
private final int m_myData;

protected MyObjectOutputStream(int myData, OutputStream out) throws IOException, SecurityException {
    super(out);
    m_myData = myData;
    initalized = true;
    writeStreamHeader();
}

protected void writeStreamHeader() throws IOException {

    if(!initalized){
        return;
    }

    write( m_myData );
    super.writeStreamHeader();
}
}

EDIT:

O, como sugiere Thilo , podría escribirse así:

public class MyObjectOutputStream extends ObjectOutputStream {

    private final int m_myData;

    protected MyObjectOutputStream(int myData, OutputStream out) throws IOException, SecurityException {
        super(out);
        m_myData = myData;
        write( m_myData );
        super.writeStreamHeader();
    }

    protected void writeStreamHeader() throws IOException {
        // work is done in the constructor
    }
}

Es generalmente una mala idea para llamar a métodos no finales del constructor (por exactamente la razón por la que se presenta).

Se puede lograr su serialización personalizada sin extender ObjectOutputStream? Estoy pensando en composición de la corriente. Por ejemplo, se puede anteponer su cabecera, escribiéndolos en el OutputStream subyacente antes ObjectOutputStream hace. Esto puede, obviamente, no puede hacer de una subclase de ObjectOutputStream, pero se puede hacer fácilmente desde el exterior.

   out.write(myExtraHeader);
   ObjectOutputStream oos = new ObjectOutputStream(out);

Si lo desea, puede envolver todo esto muy bien detrás de la interfaz ObjectOutput como Stu Thompson sugirió en su respuesta, de modo que pueda mirar al exterior casi como un ObjectOutputStream.

Actualización:

En cuanto a los JavaDocs y fuente de ObjectOutputStream, hay un segundo constructor (protegida), que no llama writeStreamHeader().

Sin embargo, este constructor también no inicializa otras estructuras internas. Para citar los documentos, se pretende "para las subclases que se reimplementar completamente ObjectOutputStream para no tener que asignar los datos privados simplemente utilizados por esta aplicación de ObjectOutputStream". En este caso también se llama a algunos otros métodos, tales como 'writeObjectOverride'. Messy ...

Uso composición en lugar de herencia.

public class MyObjOutStream implements DataOutput, ObjectOutput, ObjectStreamConstants {
    //same interfaces that ObjectOutputStream implements

    private ObjectOutputStream objOutStream;

    //implement all the methods below
}

Instancia el ObjectOutputStream sólo cuando esté listo para hacerlo. Los métodos restantes que se necesitan para implementar en las interfaces puede simplemente llamar a los mismos métodos en objOutStream

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