質問
非一時的なブールフィールドを持つ Serializable クラス (A と呼びましょう) と、同じフィールドが一時的である必要があるサブクラス (B) があります。これどうやってするの?
より正確には、B をデシリアライズするときにフィールドをデフォルトのブール値 ( false
) に復元したいのですが、A をデシリアライズするときに正しい値に復元したいと考えています。それにもかかわらず、A から継承された他のフィールドは復元する必要があります。
機能的には、A はセッション間で復元されるオブジェクトを表し、B は特定のタイプの A であり、その状態は新しいセッションごとにリセットされる必要があります。
簡単なコード サンプル:
public class A implements java.io.Serializable {
private String label;
// non-transient
private boolean field;
public String getLabel() {
return label;
}
public void setLabel(String label) {
this.label = label;
}
public boolean isField() {
return field;
}
public void setField(boolean field) {
this.field = field;
}
}
public class B extends A {
// field should be transient for this class
// label should remain non-transient
}
保持しないことを選択したいくつかの可能な解決策
簡単な解決策は、 に変更
B extends A
しA extends B
、フィールドをトランジェントにし、writeObject()
A に a を追加してフィールドをシリアル化することです。ただし、B extends A
機能的な意味があり、元に戻すことが賢明であるとは確信していません。readObject()
フィールドの逆シリアル化された値を上書きするメソッドを実装できます。ただし、これは汚い解決策のように感じます。他に選択肢がない限り、これを使用したくありません。一時的なフィールドをエミュレートするメソッドを作成しようとしました
writeObject()
が、機能せず、理由もわかりません。誰かが手がかりを持っているなら、それが私の解決策かもしれません:
public class B extends A {
private void writeObject(ObjectOutputStream out) throws IOException {
// save current state
boolean field = isField();
// synchronized to make sure this instance is not interrogated
// while changed for serialization
synchronized (this) {
// emulate transient state and serialize
setField(false);
out.defaultWriteObject();
// restore state
setField(field);
}
}
}
- 編集:シャドウイングを使用した@rocketboyのソリューションは機能しますが、未使用のフィールドが1つ残るため、シャドウイングには不快です(Aの非一時的なフィールドは使用されず、Bの一時的なバージョンは読み書きされます)。解決策かもしれませんが。経験豊富な Java 開発者は、これがクリーンなソリューションだと思いますか?
public class B extends A {
// shadow A's field
private transient boolean field;
@Override
public boolean getField() {
return field;
}
@Override
public void setField(boolean field) {
this.field = field;
}
}
答え
@ m1o2 のアドバイスに従って、Externalizableインターフェイスを使用してソリューションを実装することができました。
public class B extends A implements java.io.Externalizable {
// Do not forget to have a public no-arg constructor
// for Serialization to work
public B() {
}
@Override
public void writeExternal(ObjectOutput out) throws IOException {
// Write only the fields I am interested in
out.writeObject(getLabel());
}
@Override
public void readExternal(ObjectInput in) throws IOException,
ClassNotFoundException {
// Read the serialized fields IN THE ORDER THEY WERE WRITTEN
setLabel((String) in.readObject());
}
}
ただし、A と B は単純なクラスであるため、これが適用されることに注意してください。多くのフィールドがあり、進化する傾向があるクラスの場合、これはより多くのコストがかかる可能性があります (リフレクションに基づくコードを使用する場合を除く)。