Domanda

Il metodo ObjectOutputStream.writeStreamHeader() può essere sovrascritto per anteporre o aggiungere dati all'intestazione.Tuttavia, se tali dati si basano su un argomento passato al costruttore della classe derivata come:

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

non funziona perché super() viene chiamato prima m_myData è inizializzato e super() chiamate writeStreamHeader().L'unico modo in cui posso pensare di risolvere questo problema è utilizzare ThreadLocal Piace:

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

Sembra funzionare, ma esiste un modo migliore (meno goffo)?

È stato utile?

Soluzione

C'è un modo generico per risolvere questo tipo di problema. Rendere la classe e classe interna e riferimento una variabile nell'ambito esterno. (Nota, questo funziona solo con -target 1.4 o greter, che è l'impostazione predefinita nelle versioni correnti di javac. Con -target 1.3 si otterrà un 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();
        }
    };
}

Ma, è probabilmente più facile solo per scrivere i dati prima di costruire la ObjectOuputStream.

Altri suggerimenti

Non potresti farlo in questo modo. Ignorare la chiamata writeStreamHeader dal costruttore super e fare uno voi stessi, dopo aver inizializzato il campo necessaria:

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

Modifica

Oppure, come suggerito da Thilo , potrebbe essere scritto come:

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

In genere è una cattiva idea chiamare metodi non finali dal costruttore (esattamente per il motivo presentato).

Puoi ottenere la serializzazione personalizzata senza estendere ObjectOutputStream?Sto pensando alla composizione del flusso.Ad esempio, potresti anteporre l'intestazione scrivendola nell'OutputStream sottostante prima che lo faccia ObjectOutputStream.Questo ovviamente non può essere fatto in una sottoclasse di ObjectOutputStream, ma può essere fatto facilmente dall'esterno.

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

Se lo desideri, puoi racchiudere tutto bene dietro l'interfaccia ObjectOutput come suggerito da Stu Thompson nella sua risposta, in modo che possa apparire all'esterno quasi come un ObjectOutputStream.

Aggiornamento:

Osservando JavaDocs e il codice sorgente di ObjectOutputStream, esiste un secondo costruttore (protetto), che non chiama writeStreamHeader().

Tuttavia, questo costruttore non inizializza nemmeno altre strutture interne.Per citare i documenti, è previsto "per le sottoclassi che sono completamente reimplementando ObjectOutputStream per non dover allocare i dati privati ​​appena utilizzati da questa implementazione di ObjectOutputStream".In questo caso chiama anche altri metodi, come "writeObjectOverride".Disordinato...

Usa composizione invece di eredità.

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

    private ObjectOutputStream objOutStream;

    //implement all the methods below
}

Istanza l'ObjectOutputStream solo quando si è pronti a farlo. I metodi rimanenti è necessario implementare nelle interfacce può solo chiamare gli stessi metodi su objOutStream

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top