Pergunta

O método ObjectOutputStream.writeStreamHeader() pode ser substituído para preceder ou acrescentar dados a cabeçalho. No entanto, se essa informação é baseada em um argumento transmitido ao construtor da classe 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;
}

não funciona porque super() é chamado antes m_myData é inicializado e super() chama writeStreamHeader(). A única maneira que eu posso pensar de contornar isso é usando 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>();
}

Isso parece funcionar, mas há uma melhor (menos desajeitado) forma?

Foi útil?

Solução

Há uma maneira geral para resolver este tipo de problema. Adicione a classe e classe interna e referenciar uma variável no âmbito exterior. (Note, isso só funciona com -target 1.4 ou Greter, que é o padrão nas versões atuais do javac. Com -target 1.3 você receberá um 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();
        }
    };
}

Mas, é provavelmente mais fácil apenas para escrever os dados antes de construir o ObjectOuputStream.

Outras dicas

Você não poderia fazê-lo assim. Ignorar a chamada writeStreamHeader a partir do construtor de super e fazer um você mesmo, quando você tiver inicializado o campo necessário:

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:

Ou, como sugerido por Thilo , poderia ser escrito assim:

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

Em geral, é uma má idéia para chamar métodos não-finais do construtor (para exatamente a razão que você apresentados).

Você pode alcançar seu serialização personalizada sem estender ObjectOutputStream? Estou pensando em composição córrego. Por exemplo, você poderia preceder seu cabeçalho, escrevendo-o para o OutputStream subjacente antes ObjectOutputStream faz. Isso não pode, obviamente, ser feito de uma subclasse de ObjectOutputStream, mas pode ser feito facilmente a partir do exterior.

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

Se você quiser, você pode quebrar isso tudo muito bem por trás da interface ObjectOutput como Stu Thompson sugeriu em sua resposta, de modo que ele pode olhar para o exterior quase como um ObjectOutputStream.

Update:

Olhando para os JavaDocs e fonte para ObjectOutputStream, há um construtor segundo (protegido), que não faz writeStreamHeader() chamada.

No entanto, esse construtor também não inicializa outras estruturas internas. Para citar os docs, pretende-se "para subclasses que são reescritos completamente ObjectOutputStream para não ter de alocar dados privados apenas utilizadas por esta implementação de ObjectOutputStream". Neste caso, ele também chama alguns outros métodos, como 'writeObjectOverride'. desarrumado ...

Use composição em vez de herança.

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

    private ObjectOutputStream objOutStream;

    //implement all the methods below
}

Instância do ObjectOutputStream somente quando você está pronto para fazê-lo. Os métodos restantes você precisa implementar nas interfaces pode apenas chamar os mesmos métodos em objOutStream

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top