При проектировании классов, предназначенных для наследования (например, абстрактных классов) считается хорошим тоном не реализовывать интерфейс
Такой класс часто обязательно должен принимать в качестве параметра какой-нибудь объект, без которого он не может нормально функционировать, и предоставление конструктора по умолчанию нарушит целостность класса. В такой ситуации можно предоставить конструктор, но запретить работу с ним до момента его полноценной инициализации. Для этого можно ввести в класс состояние, и позволить вызывать его методы только в случае, если состояние этого класса — «инициализирован».
Пример из книги «Effective java»:
Serializable
, и индивидуально в каждом классе-наследнике решать, нужна ли его сериализация или нет. Однако в таком классе обязательно должен быть конструктор по умолчанию, чтобы механизмы сериализации правильно создали объект. И иногда нет возможности предоставить конструктор по умолчанию, не нарушив целостности класса.

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
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ