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
12
Опрос
Класи й статичні об'єкти,  12 уровень,  7 лекция
недоступен
Класи й статичні об'єкти
Класи й статичні об'єкти