5

メソッドObjectOutputStream.writeStreamHeader()をオーバーライドして、データをヘッダーの先頭または末尾に追加できます。ただし、そのデータが次のような派生クラスのコンストラクターに渡される引数に基づいている場合:

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

が初期化されて呼び出さsuper()れる前に呼び出されるため、機能しません。これを回避する唯一の方法は、次のように使用することです。m_myDatasuper()writeStreamHeader()ThreadLocal

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

これは機能しているように見えますが、より良い (不格好でない) 方法はありますか?

4

4 に答える 4

3

この種の問題を解決する一般的な方法があります。クラスと内部クラスを作成し、外部スコープで変数を参照します。-target 1.4(これは現在のバージョンの javac のデフォルトである以上でのみ機能することに注意してください。これを使用-target 1.3すると、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();
        }
    };
}

しかし、ObjectOuputStream.

于 2009-07-06T10:18:28.790 に答える
1

こんな風にできませんでした。必要なフィールドを初期化したら、スーパーコンストラクターからのwriteStreamHeader呼び出しを無視し、自分で呼び出します。

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

編集:

または、 Thiloが提案しているように、次のように書くことができます。

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
    }
}
于 2009-07-06T08:07:35.467 に答える
1

一般に、コンストラクターから非最終メソッドを呼び出すのは悪い考えです (まさにあなたが提示した理由から)。

ObjectOutputStream を拡張せずにカスタムのシリアル化を実現できますか? ストリーム構成を考えています。たとえば、ObjectOutputStream が行う前に、基になる OutputStream にヘッダーを書き込むことで、ヘッダーを先頭に追加できます。これは明らかに ObjectOutputStream のサブクラスでは実行できませんが、外部からは簡単に実行できます。

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

必要に応じて、Stu Thompson が彼の回答で提案したように、これをすべて ObjectOutput インターフェイスの背後にうまくラップして、ほとんど ObjectOutputStream のように外側に見えるようにすることができます。

アップデート:

JavaDocs と ObjectOutputStream のソースを見ると、 を呼び出さない 2 番目の (保護された) コンストラクターがありますwriteStreamHeader()

ただし、このコンストラクターは他の内部構造も初期化しません。ドキュメントを引用すると、「ObjectOutputStream を完全に再実装しているサブクラスが、この ObjectOutputStream の実装によって使用されたばかりのプライベート データを割り当てる必要がない」ことを意図しています。この場合、「writeObjectOverride」などの他のメソッドも呼び出します。混雑...

于 2009-07-06T07:42:56.233 に答える
0

継承の代わりに構成を使用します。

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

    private ObjectOutputStream objOutStream;

    //implement all the methods below
}

準備が整った場合にのみ ObjectOutputStream をインスタンス化します。インターフェイスに実装する必要がある残りのメソッドは、同じメソッドを呼び出すだけです。objOutStream

于 2009-07-06T07:42:21.710 に答える