Используя наследование от существующего типа, вы создаете новый тип. Этот новый тип не только содержит все члены существующего типа (хотя члены, помеченные как private, скрыты и недоступны), но и, что еще важнее, повторяет интерфейс базового класса. Значит, все сообщения, которые вы могли посылать базовому классу, вы также вправе посылать и производному классу. А так как мы различаем типы классов по совокупности сообщений, которые можем им посылать, это означает, что производный класс является частным случаем базового класса. В предыдущем примере «окружность есть фигура». Эквивалентность типов, достигаемая при наследовании, является одним из основополагающих условий понимания смысла объектно-ориентированного программирования.
Так как и базовый, и производный классы имеют одинаковый основной интерфейс, должна существовать и реализация для этого интерфейса. Другими словами, где-то должен быть код, выполняемый при получении объектом определенного сообщения. Если вы просто унаследовали класс и больше не предпринимали никаких действий, методы из интерфейса базового класса перейдут в производный класс без изменений. Это значит, что объекты производного класса не только однотипны, но и обладают одинаковым поведением, а при этом само наследование теряет смысл.
Существует два способа изменения нового класса по сравнению с базовым классом. Первый достаточно очевиден: в производный класс включаются новые методы. Они уже не являются частью интерфейса базового класса. Видимо, базовый класс не делал всего, что требовалось в данной задаче, и вы дополнили его новыми методам. Впрочем, такой простой и примитивный подход к наследованию иногда оказывается идеальным решением проблемы. Однако надо внимательно рассмотреть, действительно ли базовый класс нуждается в этих дополнительных методах. Процесс выявления закономерностей и пересмотра архитектуры является повседневным делом в объектно-ориентированном программировании.
Хотя наследование иногда наводит на мысль, что интерфейс будет дополнен новыми методами (особенно в Java, где наследование обозначается ключевым словом extends, то есть «расширять»), это совсем не обязательно. Второй, более важный способ модификации классов заключается в изменении поведения уже существующих методов базового класса. Это называется переопределением (или замещением) метода.
Для замещения метода нужно просто создать новое определение этого метода в производном классе. Вы как бы говорите: «Я использую тот же метод интерфейса, но хочу, чтобы он выполнял другие действия для моего нового типа».
Отношение «является» в сравнении с «похоже»
При использовании наследования встает очевидный вопрос: следует ли при наследовании переопределять только методы базового класса (и не добавлять новые методы, не существующие в базовом классе)? Это означало бы, что производный тип будет точно такого же типа, как и базовый класс, так как они имеют одинаковый интерфейс. В результате вы можете свободно заменять объекты базового класса объектами производных классов. Можно говорить о