1. Спільний базовий клас

Сьогодні просто день цікавих тем. Пам'ятаєте, як ми колись створили базовий клас ChessItem для спрощення всіх класів шахових фігур? Сподіваюся, що так 🙂

Тепер уявіть, що кожна фігура має метод, який займається її візуалізацією на екрані, наприклад, draw(). Викликаєш метод draw(), і фігура сама себе малює відповідно до своїх поточних координат. Цей метод було б зручно винести в базовий клас.

Якби метод draw() був у базовому класі ChessItem, можна було б перевизначити його в класах фігур і написати такий вишуканий код:

class ChessBoard
{
   public void drawAllChessItems()
   {
      // додаємо фігури до списку
      ArrayList<ChessItem> items = new ArrayList<ChessItem>();
      items.add(new King());
      items.add(new Queen());
      items.add(new Bishop());

      // малюємо їх незалежно від типу
      for(ChessItem item: items)
      {
         item.draw();
      }
   }
}

Введення базового класу ChessItem допомагає значно спростити код: не потрібно викликати методи кожного класу окремо, можна зберігати всі об'єкти в одній колекції тощо.

Проте є одне цікаве питання: а що має малювати на екрані метод draw(), оголошений безпосередньо в класі ChessItem? Адже такої фігури в шахах немає, тож і малювати немає чого.

Ба більше, створювати об'єкти типу ChessItem просто не має сенсу. Це не шахова фігура, а лише абстракція — клас, який ми створили для зручності. Так працює абстракція в ООП: ми винесли важливі (загальні для всіх фігур) дані й методи в базовий клас, а відмінності залишили в класах фігур.


2. Абстрактні класи

Абстрактні класи

На такі випадки в Java є спеціальний тип класів — абстрактні класи. Вони створені, щоб спростити програмістам роботу зі схожими класами й скоротити в них обсяг схожого коду.

Абстрактні класи мають три особливості, про які варто знати.

Метод без реалізації

Абстрактний клас може містити оголошення методу без його реалізації. Такий метод називається абстрактним. Замість тіла методу просто ставиться крапка з комою, а перед іменем методу пишуть ключове слово abstract. Приклад:

public abstract class ChessItem
{
   public int x, y; // координати
   private int value; // «цінність» фігури
   public int getValue() // звичайний метод, повертає значення value
   {
      return value;
   }

   public abstract void draw(); // абстрактний метод. Реалізації немає.
}

Абстрактний клас

Кожен метод без реалізації позначається спеціальним ключовим словом abstract. Якщо в класі є принаймні один абстрактний метод, клас теж позначається ключовим словом abstract.

Заборона на створення об'єктів

Створювати об'єкти абстрактного класу не можна. Такий код просто не скомпілюється.

Код Опис
ChessItem item = new ChessItem();
item.draw();
Цей код не скомпілюється
ChessItem item = new Queen();
item.draw();
А так можна

Успадкування від абстрактного класу

Якщо новий клас успадковано від абстрактного класу, потрібно перевизначити всі успадковані абстрактні методи, тобто написати для них реалізацію. Інакше такий клас теж доведеться оголосити абстрактним.

Якщо клас має принаймні один нереалізований метод, оголошений безпосередньо в ньому або успадкований від батьківського класу, такий клас вважається абстрактним.

І навіщо нам потрібні абстрактні класи? Хіба не можна замість них використовувати звичайні? А замість абстрактних методів писати методи, тіло яких складається лише з двох дужок — {}?

Можна. Але ці обмеження подібні на модифікатор private. Ми навмисне забороняли за допомогою private прямий доступ до даних, щоб інші програмісти та їхні класи користувалися тільки нашими public-методами.

Так само й з абстрактним класом. Той, хто написав цей клас, не хоче, щоб створювалися об'єкти цього класу. Навпаки, він розраховує на те, що абстрактні методи його абстрактного класу будуть успадковуватися й перевизначатися.

Перевага цього підходу проявляється у великих проєктах. Що більше класів, то чіткіше доводиться визначати їхні ролі. Ви побачите перевагу цього підходу вже в найближчому майбутньому. Усі через це проходять.