1. Пегас
Давайте поглубже разберем третий принцип ООП — наследование. Это очень интересная тема, которой вы будете пользоваться часто. Программирование, для несведущих, неотличимо от магии. Поэтому начнем с такой интересной аналогии...;
Предположим, что вы – волшебник и хотите создать летающую лошадь. С одной стороны, вы бы могли попробовать наколдовать пегаса. Но т.к. пегасов в природе не существует, это будет очень непросто. Придется очень многое делать самому. Куда проще взять лошадь и приколдовать ей крылья.
В программировании такой процесс называется «наследование». Предположим, вам нужно написать очень сложный класс. Писать с нуля долго, потом еще долго все тестировать и искать ошибки. Зачем идти самым сложным путем? Лучше поискать, а нет ли уже такого класса?
Предположим, вы нашли класс, который своими методами реализует 80% нужной вам функциональности. Что делать с ним дальше? Вы можете просто скопировать его код в свой класс. Но у такого решения есть несколько минусов:
- Найденный класс уже может быть скомпилирован в байт-код, а доступа к его исходному коду у вас нет.
- Исходный код класса есть, но вы работаете в компании, которую могут засудить на пару миллиардов за использование даже 6 строчек чужого кода. А потом она засудит вас.
- Ненужное дублирование большого объема кода. Кроме того, если автор чужого класса найдет в нем ошибку и исправит ее, у вас эта ошибка останется.
Есть решение потоньше, и без необходимости получать легальный доступ к коду оригинального класса. В Java вы можете просто объявить тот класс родителем вашего класса. Это будет эквивалентно тому, что вы добавили код того класса в код своего. В вашем классе появятся все данные и все методы класса-родителя. Например, можно делать так: наследуемся от «лошади», добавляем «крылья» – получаем «пегаса»
2. Общий базовый класс
Наследование можно использовать и для других целей. Допустим, у вас есть десять классов, которые очень похожи, имеют совпадающие данные и методы. Вы можете создать специальный базовый класс, вынести эти данные (и работающие с ними методы) в этот базовый класс и объявить те десять классов его наследниками. Т.е. указать в каждом классе, что у него есть класс-родитель — данный базовый класс.
Также как преимущества абстракции раскрываются только рядом с инкапсуляцией, так и преимущества наследования гораздо сильнее при использовании полиморфизма. Но о нем вы узнаете немного позже. Сегодня же мы рассмотрим несколько примеров использования наследования.
Шахматные фигуры
Предположим, мы пишем программу, которая играет в шахматы с пользователем, а значит, нам понадобятся классы для фигур. Что бы это были за классы?
Очевидный ответ, если вы когда-нибудь играли в шахматы — Король, Ферзь, Слон, Конь, Ладья и Пешка.
Но в самих классах еще нужно было бы хранить информацию по каждой фигуре. Например, координаты x и y, а также ценность фигуры. Ведь некоторые фигуры ценнее других.
Кроме того, фигуры ходят по-разному, а значит и поведение классов будет отличаться. Вот как можно было бы описать их в виде классов:
|
|
|
|
|
|
Это очень примитивное описание шахматных фигур.
Общий базовый класс
А вот как можно было бы сократить код с помощью наследования. Мы могли бы вынести одинаковые методы и данные в общий класс. Назовем его ChessItem
. Объекты класса ChessItem
не имеет смысла создавать, так как ему не соответствует ни одна шахматная фигура, но от него было бы много пользы:
|
|
|
|
||
|
|
|
Это отличный способ упростить код похожих объектов. Особенно много преимуществ мы получаем, когда в проекте тысячи различных объектов и сотни классов. Тогда правильно подобранными родительскими (базовыми) классами можно не только существенно упростить логику, но и сократить код в десятки раз.
3. Наследование класса — extends
Так что же нужно, чтобы унаследовать какой-то класс? Чтобы унаследовать один класс от другого, нужно после объявления нашего класса указать ключевое слово extends
и написать имя родительского класса. Выглядит это обычно примерно так:
class Потомок extends Родитель
Именно такую конструкцию нужно написать при объявлении класса Потомок. Наследоваться, кстати, можно только от одного класса.
На картинке мы видим «корову», унаследованную от «свиньи». «Свинья» унаследована от «курицы», «курица» от «яйца». Только один родитель! Такое наследование не всегда логично. Но если есть только свинья, а очень нужна корова, программист зачастую не может устоять перед желанием сделать «корову» из «свиньи».
В Java нет множественного наследования: нельзя унаследовать класс от двух классов. У каждого класса может быть только один класс-родитель. Если класс-родитель не указан, таковым считается класс Object
.
Хотя в Java есть множественное наследование интерфейсов. Это немного снижает остроту проблемы. Про интерфейсы мы поговорим немного позже, а пока давайте продолжим разбираться с наследованием.
P.S.
Вот вам несколько историй, о том, как часто приходится делать из мухи свинью. И что за это бывает:
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ