Привіт! Давай поговоримо про абстрактні класи в Java.
![Абстрактні класи в Java на конкретних прикладах - 1]()
Саме через таку «незрозумілість», коли об'єкту неясно, яку поведінку він має обрати, творці Java відмовилися від множинного успадкування. Втім, ти дізнаєшся, що Java класи реалізують безліч інтерфейсів.

Чому класи називають «абстрактними»
Ти, напевно, пам'ятаєш, що таке «абстракція» — ми це вже проходили :) Якщо раптом забулося — не страшно, згадаємо: це принцип ООП, згідно з яким при проєктуванні класів та створенні об'єктів необхідно виокремлювати лише головні властивості сутності, та відкидати другорядні. Наприклад, якщо ми проєктуємо класSchoolTeacher
— шкільний вчитель — навряд чи знадобиться характеристика «зріст». Справді: для викладача ця характеристика не є важливою. Але якщо ми будемо створювати в програмі клас BasketballPlayer
— баскетбольний гравець — зріст стане однією з основних характеристик.
Так ось, абстрактний клас — це максимально абстрактна, ду-у-уже приблизна «заготівля» для групи майбутніх класів. Цю заготівлю не можна використовувати у готовому вигляді — надто «сира». Але вона описує певний загальний стан і поведінку, які матимуть майбутні класи — спадкоємці абстрактного класу.
Приклади абстрактних класів Java
Розглянемо простий приклад із автівками:
public abstract class Car {
private String model;
private String color;
private int maxSpeed;
public abstract void gas();
public abstract void brake();
public String getModel() {
return model;
}
public void setModel(String model) {
this.model = model;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
public int getMaxSpeed() {
return maxSpeed;
}
public void setMaxSpeed(int maxSpeed) {
this.maxSpeed = maxSpeed;
}
}
Ось так виглядає найпростіший абстрактний клас. Як бачиш, нічого особливого :)
Навіщо він може нам знадобитися?
Насамперед він максимально абстрактно описує потрібну нам сутність — автомобіль. Слово abstract тут недарма. У світі немає «просто машин». Є вантажівки, гоночні автомобілі, седани, купе, позашляховики.
Наш абстрактний клас — це просто «креслення», з яким ми пізніше будемо створювати класи-автомобілі.
public class Sedan extends Car {
@Override
public void gas() {
System.out.println("Седан газує!");
}
@Override
public void brake() {
System.out.println("Седан гальмує!");
}
}
Це схоже на те, про що ми говорили в лекціях про спадкування. Тільки там у нас клас Car
та його методи були абстрактними. Але таке рішення має цілу низку мінусів, які в абстрактних класах виправлені.
Перше і головне — екземпляр абстрактного класу створити не можна:
public class Main {
public static void main(String[] args) {
Car car = new Car(); // Помилка! Клас Car є абстрактним!
}
}
Ця «фішка» була реалізована творцями Java спеціально. Ще раз, для запам'ятовування: абстрактний клас - це просто креслення для майбутніх «нормальних» класів. Тобі ж не потрібні екземпляри креслення, правильно? Ось і екземпляри абстрактного класу створювати не треба :)
А якщо б клас Car
не був абстрактним, ми легко могли б створювати його об'єкти:
public class Car {
private String model;
private String color;
private int maxSpeed;
public void gas() {
// якась логіка
}
public void brake() {
// якась логіка
}
}
public class Main {
public static void main(String[] args) {
Car car = new Car(); // Все ок, авто створено
}
}
Тепер у нас у програмі з'явилася якась незрозуміла машина — не вантажівка, не гоночна, не седан, а не зрозуміло що. Та сама «просто машина», яких у природі немає.
Той самий приклад можна навести з тваринами. Уяви, якби у твоїй програмі з'явилися об'єкти Animal
— «просто тварина». Якого воно виду, до якого сімейства належить, які в неї характеристики — неясно. Було б дивно побачити його у програмі. Жодних «просто тварин» у природі не існує. Тільки собаки, кішки, лисиці, кроти та інші.
Абстрактні класи позбавляють нас від «просто об'єктів». Вони дають нам базовий стан та поведінку. Наприклад, у всіх машин має бути модель, колір і максимальна швидкість, а ще вони повинні вміти газувати та гальмувати. Ось і все. Це — загальна абстрактна схема, далі ти сам проектуєш потрібні тобі класи.
Зверни увагу: два методи в абстрактному класі теж позначені як abstract, і вони взагалі не реалізовані. Причина та ж: абстрактні класи не створюють «за замовчуванням» для «просто машин». Вони просто кажуть, що мають вміти робити усі машини.
Втім, якщо поведінка за замовчуванням тобі все ж таки потрібна, методи в абстрактному класі можна реалізувати. Java цього не забороняє:
public abstract class Car {
private String model;
private String color;
private int maxSpeed;
public void gas() {
System.out.println("Газуємо!");
}
public abstract void brake();
//геттери і сеттери
}
public class Sedan extends Car {
@Override
public void brake() {
System.out.println("Седан гальмує!");
}
}
public class Main {
public static void main(String[] args) {
Sedan sedan = new Sedan();
sedan.gas();
}
}
Виведення в консолі:
“Газуємо!”
Як бачиш, ми реалізували в абстрактному класі один метод, а другий не стали.
У результаті поведінка нашого класу Sedan
розділилася на дві частини: якщо викликати в нього метод gas()
, він «підтягнеться» з батьківського абстрактного класу Car
, а метод brake()
ми перевизначили у класі Sedan
. Вийшло дуже зручно та гнучко.
Але тепер наш клас не такий вже й абстрактний? Адже в нього, за фактом, половина методів реалізована.
Насправді, і це дуже важлива особливість — клас є абстрактним, якщо хоча б один із його методів є абстрактним. Хоч один із двох, хоч один із тисячі методів — не має значення.
Ми можемо навіть реалізувати всі методи та не залишити жодного абстрактного. Буде абстрактний клас без абстрактних методів. В принципі, це можливо, і компілятор не видасть помилок, але краще так не робити: слово abstract втратить сенс, а твої колеги-програмісти дуже здивуються, побачивши таке :/
При цьому, якщо метод позначений словом abstract, кожен клас-спадкоємець має його реалізувати або бути оголошеним як абстрактний. Інакше компілятор викине помилку.
Зрозуміло, кожен клас може успадкуватися лише від одного абстрактного класу, отже в плані спадкування різниці між абстрактними та звичайними класами немає. Не має значення, успадковуємося ми від абстрактного класу або від звичайного, клас-батько може бути лише один.
Чому в Java немає множинного успадкування класів
Ми вже говорили, що в Java немає множинного успадкування, але належним чином не розібралися, чому саме. Давайте спробуємо зробити це зараз. Справа в тому, що якби в Java було множинне успадкування, дочірні класи не могли б визначитися, яку саме поведінку обрати. Припустимо, у нас є два класи —Toster
и NuclearBomb
:
public class Toster {
public void on() {
System.out.println("Тостер увімкнений, тост готується!");
}
public void off() {
System.out.println("Тостер вимкнено!");
}
}
public class NuclearBomb {
public void on() {
System.out.println("Вибух!");
}
}
Як бачиш, у обох є метод on()
. У випадку з тостером він запускає приготування тосту, а у випадку з ядерною бомбою влаштовує вибух.
Ой :/
А тепер уяви, що ти вирішив (не знаю, з чого раптом!) створити щось середнє між ними. І ось він твій клас — MysteriousDevice
!
Цей код, зрозуміло, не робочий, і ми наводимо його просто як приклад «а як воно могло б бути»:
public class MysteriousDevice extends Toster, NuclearBomb {
public static void main(String[] args) {
MysteriousDevice mysteriousDevice = new MysteriousDevice();
mysteriousDevice.on(); // І що тут має статися? Ми отримаємо тост чи ядерний апокаліпсис?
}
}
Погляньмо, що в нас вийшло. Загадковий пристрій походить одночасно і від Тостера і від Ядерної Бомби. В обох є метод on()
, і в результаті незрозуміло, який із методів on()
повинен спрацьовувати в об'єкта MysteriousDevice
, якщо ми його викличемо. Об'єкт ніяк не зможе цього зрозуміти.
Ну і як вишенька на торті: у Ядерної Бомби немає методу off()
, так що якщо ми не вгадали, відключити пристрій буде неможливо.

ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ