Наследование очень полезно, но также нарушает инкапсуляцию. Это означает, что ваши подклассы зависят от деталей реализации суперкласса; если суперкласс изменится, ваш подкласс может сломаться. Вот пример на Java от Effective Java Джоша Блоха:
public class InstrumentedHashSet<E> extends HashSet<E> {
// number of attempted element insertions
private int addCount = 0;
public int getAddCount() {
return addCount;
}
@Override public boolean addAll<Collection<? extends E> c) {
addCount += c.size();
return super.addAll(c);
}
}
Проблема в том, что метод addAll () в HashSet внутренне использует метод add (), но не документирует это. Так что если вы попробуете
InstrumentedHashSet<String> s = new InstrumentedHashSet<String>();
s.addAll(Arrays.asList("Snap", "Crackle", "Pop"));
в итоге вы получите счет 6 вместо 3. В данном конкретном случае это не очень вредно, но если вы добавляете большую коллекцию или выполняете какую-либо другую операцию, это может быть.
Таким образом, конкретные классы, как правило, не являются хорошей идеей для наследования, если они не предназначены для подклассов.