1. Пегас

Давайте поглубже разберем третий принцип ООПнаследование. Это очень интересная тема, которой вы будете пользоваться часто. Программирование, для несведущих, неотличимо от магии. Поэтому начнем с такой интересной аналогии...;

Предположим, что вы – волшебник и хотите создать летающую лошадь. С одной стороны, вы бы могли попробовать наколдовать пегаса. Но т.к. пегасов в природе не существует, это будет очень непросто. Придется очень многое делать самому. Куда проще взять лошадь и приколдовать ей крылья.

Наследование. ООП

В программировании такой процесс называется «наследование». Предположим, вам нужно написать очень сложный класс. Писать с нуля долго, потом еще долго все тестировать и искать ошибки. Зачем идти самым сложным путем? Лучше поискать, а нет ли уже такого класса?

Предположим, вы нашли класс, который своими методами реализует 80% нужной вам функциональности. Что делать с ним дальше? Вы можете просто скопировать его код в свой класс. Но у такого решения есть несколько минусов:

  1. Найденный класс уже может быть скомпилирован в байт-код, а доступа к его исходному коду у вас нет.
  2. Исходный код класса есть, но вы работаете в компании, которую могут засудить на пару миллиардов за использование даже 6 строчек чужого кода. А потом она засудит вас.
  3. Ненужное дублирование большого объема кода. Кроме того, если автор чужого класса найдет в нем ошибку и исправит ее, у вас эта ошибка останется.

Есть решение потоньше, и без необходимости получать легальный доступ к коду оригинального класса. В Java вы можете просто объявить тот класс родителем вашего класса. Это будет эквивалентно тому, что вы добавили код того класса в код своего. В вашем классе появятся все данные и все методы класса-родителя. Например, можно делать так: наследуемся от «лошади», добавляем «крылья» – получаем «пегаса»

Наследование. ООП


2. Общий базовый класс

Наследование можно использовать и для других целей. Допустим, у вас есть десять классов, которые очень похожи, имеют совпадающие данные и методы. Вы можете создать специальный базовый класс, вынести эти данные (и работающие с ними методы) в этот базовый класс и объявить те десять классов его наследниками. Т.е. указать в каждом классе, что у него есть класс-родитель — данный базовый класс.

Также как преимущества абстракции раскрываются только рядом с инкапсуляцией, так и преимущества наследования гораздо сильнее при использовании полиморфизма. Но о нем вы узнаете немного позже. Сегодня же мы рассмотрим несколько примеров использования наследования.

Шахматные фигуры

Предположим, мы пишем программу, которая играет в шахматы с пользователем, а значит, нам понадобятся классы для фигур. Что бы это были за классы?

Очевидный ответ, если вы когда-нибудь играли в шахматы — Король, Ферзь, Слон, Конь, Ладья и Пешка.

Но в самих классах еще нужно было бы хранить информацию по каждой фигуре. Например, координаты x и y, а также ценность фигуры. Ведь некоторые фигуры ценнее других.

Кроме того, фигуры ходят по-разному, а значит и поведение классов будет отличаться. Вот как можно было бы описать их в виде классов:

class King
{
   int x;
   int y;
   int worth;

   void kingMove()
   {
     // код, решающий,
     // как пойдет
     // король
   }
}
class Queen
{
   int x;
   int y;
   int worth;

   void queenMove()
   {
     // код, решающий,
     // как пойдет
     // ферзь
   }
}
class Rook
{
   int x;
   int y;
   int worth;

   void rookMove()
   {
     // код, решающий,
     // как пойдет
     // ладья
   }
}
class Knight
{
   int x;
   int y;
   int worth;

   void knightMove()
   {
     // код, решающий,
     // как пойдет
     // конь
   }
}
class Bishop
{
   int x;
   int y;
   int worth;

   void bishopMove()
   {
     // код, решающий,
     // как пойдет
     // слон
   }
}
class Pawn
{
   int x;
   int y;
   int worth;

   void pawnMove()
   {
     // код, решающий,
     // как пойдет
     // пешка
   }
}

Это очень примитивное описание шахматных фигур.

Общий базовый класс

А вот как можно было бы сократить код с помощью наследования. Мы могли бы вынести одинаковые методы и данные в общий класс. Назовем его ChessItem. Объекты класса ChessItem не имеет смысла создавать, так как ему не соответствует ни одна шахматная фигура, но от него было бы много пользы:

class King extends ChessItem
{
   void kingMove()
   {
     // код, решающий,
     // как пойдет король
   }
}
class Queen extends ChessItem
{
   void queenMove()
   {
     // код, решающий,
     // как пойдет ферзь
   }
}
class Rook extends ChessItem
{
   void rookMove()
   {
     // код, решающий,
     // как пойдет ладья
   }
}
class ChessItem
{
   int x;
   int y;
   int worth;
}
class Knight extends ChessItem
{
   void knightMove()
   {
     // код, решающий,
     // как пойдет конь
   }
}
class Bishop extends ChessItem
{
   void bishopMove()
   {
     // код, решающий,
     // как пойдет слон
   }
}
class Pawn extends ChessItem
{
   void pawnMove()
   {
     // код, решающий,
     // как пойдет пешка
   }
}

Это отличный способ упростить код похожих объектов. Особенно много преимуществ мы получаем, когда в проекте тысячи различных объектов и сотни классов. Тогда правильно подобранными родительскими (базовыми) классами можно не только существенно упростить логику, но и сократить код в десятки раз.


3. Наследование класса — extends

Так что же нужно, чтобы унаследовать какой-то класс? Чтобы унаследовать один класс от другого, нужно после объявления нашего класса указать ключевое слово extends и написать имя родительского класса. Выглядит это обычно примерно так:

class Потомок extends Родитель

Именно такую конструкцию нужно написать при объявлении класса Потомок. Наследоваться, кстати, можно только от одного класса.

Наследование класса – extends

На картинке мы видим «корову», унаследованную от «свиньи». «Свинья» унаследована от «курицы», «курица» от «яйца». Только один родитель! Такое наследование не всегда логично. Но если есть только свинья, а очень нужна корова, программист зачастую не может устоять перед желанием сделать «корову» из «свиньи».

В Java нет множественного наследования: нельзя унаследовать класс от двух классов. У каждого класса может быть только один класс-родитель. Если класс-родитель не указан, таковым считается класс Object.

Хотя в Java есть множественное наследование интерфейсов. Это немного снижает остроту проблемы. Про интерфейсы мы поговорим немного позже, а пока давайте продолжим разбираться с наследованием.

P.S.

Вот вам несколько историй, о том, как часто приходится делать из мухи свинью. И что за это бывает:


undefined
8
Задача
Java Syntax Pro, 8 уровень, 7 лекция
Недоступна
Простое наследование
Построй правильную цепочку наследования классов. Женщина должна наследоваться от человека, а человек от землянина.
undefined
8
Задача
Java Syntax Pro, 8 уровень, 7 лекция
Недоступна
Наследование переменных
Правильно унаследуй классы: - машину — от транспортного средства; - электрокар — от машины. Удали дублирующиеся переменные.
undefined
8
Задача
Java Syntax Pro, 8 уровень, 7 лекция
Недоступна
Наследование методов
Правильно унаследуй классы: - человека — от существа; - Java-девелопера — от человека. Удали дублирующие методы.