Ответ
Нет, не всегда. Наследник может служить полноценной заменой родителя только при соблюдении Принципа подстановки Барбары Лисков (Liskov Substitution Principle, LSP). Этот принцип гласит, что объекты базового класса должны быть заменяемы объектами производного класса без изменения корректности программы.
Ключевые условия LSP:
- Наследник не должен ужесточать предусловия (требования к входным данным).
- Наследник не должен ослаблять постусловия (гарантии на результат и состояние).
- Наследник не должен генерировать новые типы исключений, не являющихся подтипами исключений базового класса.
- Наследник должен сохранять или усиливать инварианты базового класса.
Классический пример нарушения LSP:
class Rectangle {
protected int width, height;
public void setWidth(int w) { width = w; }
public void setHeight(int h) { height = h; }
public int getArea() { return width * height; }
}
// Квадрат — это частный случай прямоугольника с математической точки зрения,
// но с точки зрения поведения объектов это нарушение LSP.
class Square extends Rectangle {
@Override
public void setWidth(int w) {
super.setWidth(w);
super.setHeight(w); // Неожиданный побочный эффект!
}
@Override
public void setHeight(int h) {
super.setHeight(h);
super.setWidth(h); // Неожиданный побочный эффект!
}
}
// Клиентский код, работающий с Rectangle, сломается:
void testArea(Rectangle r) {
r.setWidth(5);
r.setHeight(4);
assert r.getArea() == 20; // Упадет, если r — Square (площадь будет 16).
}
Вывод: Наследование следует использовать только при истинном отношении "is-a", где подкласс является специализацией, полностью сохраняющей контракт базового класса.