継承を目的としたクラス (抽象クラスなど) を設計する場合は、 インターフェース を実装せず、
Serializable
直列化する必要があるかどうかを各子孫クラスで個別に決定することをお勧めします。ただし、シリアル化メカニズムがオブジェクトを正しく作成するには、そのようなクラスにはデフォルトのコンストラクターが必要です。また、クラスの整合性を損なうことなくデフォルトのコンストラクターを提供する方法がない場合もあります。 このようなクラスは、多くの場合、パラメータとして何らかのオブジェクトを取得する必要があり、それがないと正常に機能しません。また、デフォルトのコンストラクタを提供すると、クラスの整合性に違反します。このような状況では、コンストラクターを提供できますが、完全に初期化されるまではコンストラクターを使用した作業を禁止します。これを行うには、クラスに状態を導入し、このクラスの状態が「初期化」されている場合にのみメソッドの呼び出しを許可します。「Effective Java」という本からの例:
public abstract class AbstractFoo {
private int x, y;
private enum State {
NEW, INITIALIZING, INITIALIZED
};
private final AtomicReference<State> init = new AtomicReference<State>(State.NEW);
protected AbstractFoo() {}
public AbstractFoo(int x, int y) {
initialize(x, y);
}
protected final void initialize(int x, int y) {
if (!init.compareAndSet(State.NEW, State.INITIALIZING)) {
throw new IllegalStateException("Already initialized");
}
this.x = x;
this.y = y;
init.set(State.INITIALIZED);
}
protected final int getX() {
checkInit();
return x;
}
protected final int getY() {
checkInit();
return y;
}
private void checkInit() {
if (init.get() != State.INITIALIZED) {
throw new IllegalStateException("Uninitialized");
}
}
}
class Foo extends AbstractFoo implements Serializable {
private void readObject(ObjectInputStream s) throws IOException,
ClassNotFoundException {
s.defaultReadObject();
int x = s.readInt();
int y = s.readInt();
initialize(x, y);
}
private void writeObject(ObjectOutputStream s) throws IOException {
s.defaultWriteObject();
s.writeInt(getX());
s.writeInt(getY());
}
public Foo(int x, int y) {
super(x, y);
}
}
同時に、このクラスのすべてのメソッドはクラスの現在の状態をチェックして不正な使用を防止する必要があり、メソッドreadObject()
とメソッドは子孫クラスで定義する必要がありますwriteObject()
。元のソースへのリンク: http://0agr.ru
GO TO FULL VERSION