JavaRush /Курси /Java Syntax Zero /Наслідування

Наслідування

Java Syntax Zero
Рівень 8 , Лекція 7
Відкрита

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.

Ось тобі кілька історій, про те, як часто доводиться робити зі звичайного об'єкта виняток. І що за це буває:


Коментарі (8)
ЩОБ ПОДИВИТИСЯ ВСІ КОМЕНТАРІ АБО ЗАЛИШИТИ КОМЕНТАР,
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ
Potapchuk Рівень 13
4 вересня 2023
Посилання https://voron-vp.livejournal.com/42033.html - НЕ ПРАЦЮЄ!!! =(
Sava_crosava Рівень 23
12 вересня 2023
На жаль, цей сервіс попав під блокування) Стаття досить цікава, тому використай англомовний контент або ж юзай VPN(
Валерій Рівень 52
13 лютого 2025
працює через VPN
Vitalii Рівень 11
10 серпня 2023
нащо вони в кожній фігурі в назві метода додатково пишуть назву класа? це типу для тих що з першого разу не розуміє? Якщо він напише king.kingMove() то приходить якось краще?
Саша Рівень 30
2 жовтня 2022
Чому правильна відповідь не виконує завдання? Перечитував все, але присвоєння класу не найшов
Roma Chernesh Рівень 16
20 грудня 2022
Та нормально все працює) Хіба довчився би хтось до 16 рівня, якби тут все так погано було? :)
Mykola Рівень 15 Expert
8 серпня 2023
Roma Chernesh, Закинув?
Julia Рівень 23
14 грудня 2024
Mykola, закинув?)