Frage

Das Verfahren ObjectOutputStream.writeStreamHeader() kann überschrieben werden, um Daten in den Header voranstellen oder anfügen. Wenn jedoch die Daten basiert auf einem Argument der abgeleiteten Klasse Konstruktor übergeben wie:

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

es funktioniert nicht, weil super() aufgerufen wird, bevor m_myData initialisiert und super() ruft writeStreamHeader(). Die einzige Möglichkeit, die ich denken kann, dies zu umgehen, ist die Verwendung ThreadLocal wie:

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

Das scheint zu funktionieren, aber gibt es eine bessere (weniger klobig) Art und Weise?

War es hilfreich?

Lösung

Es gibt eine allgemeine Art und Weise, diese Art von Problem zu lösen. Machen Sie die Klasse und innere Klasse und eine Variable in dem äußeren Umfang verweisen. (Beachten Sie, das funktioniert nur mit -target 1.4 oder greter, die Standardeinstellung in den aktuellen Versionen von javac ist. Mit -target 1.3 Sie werden NPE erhalten.)

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

Aber, ist es wahrscheinlich einfacher, nur die Daten zu schreiben, bevor die ObjectOuputStream konstruieren.

Andere Tipps

Könnten Sie nicht tun es wie folgt. Ignorieren Sie die writeStreamHeader Anruf aus dem Super-Konstruktor und führen Sie einen selbst, wenn Sie das benötigte Feld initialisiert:

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:

Oder, wie von Thilo vorgeschlagen, könnte es wie geschrieben werden:

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 ist generell eine schlechte Idee nicht-final-Methoden aus dem Konstruktor aufzurufen (genau der Grund, warum Sie vorgelegt).

Können Sie Ihre benutzerdefinierte Serialisierung erreichen, ohne Object erstreckt? Ich denke an Stromzusammensetzung. Zum Beispiel könnten Sie Ihre Header, indem sie es auf den zugrunde liegenden Output Schreiben vor Object tut voranstellen. Dies kann natürlich nicht in einer Unterklasse von Object getan werden, aber es kann leicht von außen durchgeführt werden.

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

Wenn Sie möchten, können Sie diese wickeln alle oben schön hinter dem ObjectOutput Schnittstelle als Stu Thompson in seiner Antwort vorgeschlagen, so dass es fast nach außen aussehen kann wie ein Object.

Update:

an den JavaDocs Suchen und Quelle für Object, ein zweiter ist (geschützt) Konstruktor, die nicht writeStreamHeader() nicht nennen.

Dies ist jedoch Konstruktor auch nicht initialisiert andere interne Strukturen. Um die Dokumente zu zitieren, es ist beabsichtigt, „für Unterklassen, die Object vollständig neu implementieren sind nicht private Daten verwendet nur durch diese Implementierung von Object“. In diesem Fall ist es ruft auch einige andere Methoden, wie zum Beispiel‚zuordnen müssen writeObjectOverride‘. Messy ...

Verwenden Zusammensetzung anstelle der Vererbung.

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

    private ObjectOutputStream objOutStream;

    //implement all the methods below
}

Instanz der Object nur, wenn Sie bereit sind, dies zu tun. Die übrigen Methoden müssen Sie in den Schnittstellen implementieren können nur die gleichen Methoden auf objOutStream rufen

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top