ObjectOutputStream.writeStreamHeader() をオーバーライドするにはどうすればよいですか?
-
23-08-2019 - |
質問
方法 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_myData
初期化されていて、 super()
電話 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>();
}
これは機能するようですが、もっと良い(あまり不格好ではない)方法はありますか?
解決
この種の問題を解決する一般的な方法があります。クラスや内部クラスを作成し、外側のスコープで変数を参照。 (これが唯一のjavacの現在のバージョンではデフォルトである、-target 1.4
またはgreterで動作し、注意してください。-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
を構築する前にデータを書き出すために、おそらく簡単です。
他のヒント
こんなことはできなかったのか。スーパー コンストラクターからの 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();
}
}
編集:
または、提案されているように、 ティロ, 、次のように書くことができます。
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
}
}
一般に、コンストラクターから非finalメソッドを呼び出すことは悪い考えです(まさにあなたが提示した理由により)。
ObjectOutputStream を拡張せずにカスタム シリアル化を実現できますか?ストリーム構成を考えています。たとえば、ObjectOutputStream が実行する前にヘッダーを基になる OutputStream に書き込むことで、ヘッダーを先頭に追加できます。これは明らかに ObjectOutputStream のサブクラス内では実行できませんが、外部から簡単に実行できます。
out.write(myExtraHeader);
ObjectOutputStream oos = new ObjectOutputStream(out);
必要に応じて、Stu Thompson が回答で示唆したように、これを ObjectOutput インターフェイスの背後でうまくまとめて、外側からは ObjectOutputStream のように見えるようにすることができます。
アップデート:
JavaDocs と ObjectOutputStream のソースを見ると、呼び出しを行わない 2 番目の (保護された) コンストラクターがあります。 writeStreamHeader()
.
ただし、このコンストラクターは他の内部構造も初期化しません。ドキュメントを引用するには、 これは、「ObjectOutputStreamを完全に再実装するサブクラス向け」を意図しています ObjectOutputStreamのこの実装によってのみ使用されるプライベート・データを割り当てる必要がないため」を参照してください。この場合、「writeObjectOverride」などの他のメソッドも呼び出します。乱雑な...
構図の代わりに、継承を使用してください。
public class MyObjOutStream implements DataOutput, ObjectOutput, ObjectStreamConstants {
//same interfaces that ObjectOutputStream implements
private ObjectOutputStream objOutStream;
//implement all the methods below
}
インスタンスあなたがそうする準備ができているだけにObjectOutputStream。残りのメソッドは、あなただけのobjOutStream
で同じメソッドを呼び出すことができるインタフェースで実装する必要があります。