Об’єктно-орієнтоване програмування (продовження)
Одразу перейдемо до теми.
Успадкування
У вступі до ООП розповідалось про те, що клас може наслідувати інший, розширюючи його. Такі підкласи мають окрім полей і методів суперкласу, ще й свої власні. Для наслідування необхідно після назви класу вказати слово extends
і назву батьківського класу:
class A {
protected int a;
}
class B extends A {
protected int b;
}
У класі “А” є лише поле a
, а у класі “B” є поля a
і b
. Зверніть увагу: для того щоб дані класу передались дочірньому, вони повинні бути хоча б захищеними — protected
(або відкритими — public
), як видно з таблиці у першій частині ООП!
super
Якщо підклас перезаписує метод або перевизначає поле, то може бути потреба звернутись до методу (поля) суперкласу — для цього є слово super
. Воно є схожим на this
, тому використовується майже так само:
class A {
public void print(){
System.out.println("Метод суперкласу");
}
} class B extends A {
@Override
public void print(){
super.print();
System.out.println("Метод підкласу");
}
}
B obj = new B();
obj.print(); //Виведе "Метод суперкласу" і "Метод підкласу"
@Override
— це анотація, що вказує Java, що ми дійсно бажаємо перезаписати метод print()
, а не просто помилилися, вказавши таку ж назву. Звичайно, можна використовувати super()
як функцію для виклику конструктора суперкласу.
Абстрагування
Абстракція
Буває й таке, що декілька класів мають спільні риси, тому для спрощення роботи з цими класами існує абстрагування — відокремлення деяких значень класів з метою приведення до загального вигляду. Наприклад, є класи “Трикутник” і “Квадрат”. Обидва мають площу та периметр, адже вони є фігурами. Для цього у Java існують абстрактні класи й методи. Абстракція значить, що у загальному вигляді методи нічого не роблять, а кожен клас, який наслідує абстрактний клас, має свою імплементацію цього методу. Створити об’єкт абстрактного класу неможливо. Абстрактним є клас, що має хоч один абстрактний метод, але не обов’язково всі. Нехай буде абстрактний клас (визначається словом abstract
) з масивом довжини сторін та методами обчислення площі й периметра. Периметр обчислюється однаково, лише додаванням довжини всіх сторін, а от площа по-різному, тому метод обчислення площі буде абстрактним:
abstract class Shape {
public int[] sides;
public Shape(int[] sides) {
this.sides = sides;
}
public abstract float calcArea();
public int calcPerimeter() {
int p = 0;
for(int side : sides) p += side;
return p;
}
}
Квадрат, який наслідує Фігуру, має перезаписати абстрактний метод знаходження площі, а всі інші методи та поля автоматично передадуться йому:
class Square {
public float calcArea() {
return Math.pow(this.sides[0], 2);
}
}
То ж, тепер можна створити новий квадрат і обчислити його периметр і площу:
Square sq = new Square(new int[]{2, 2, 2, 2});
System.out.println( sq.calcArea() );
VarArgs
Здається, не дуже зручно писати масиви в аргументах. Ця проблема, на щастя, має просте рішення! Якщо аргументів одного типу може бути 0 або більше (невизначена кількість), то можна використати список аргументів змінної довжини, або простіше varargs — ...
після типу. Таким чином, конструктор Фігури можна замінити на такий:
public Shape(int... sides) {
this.sides = sides;
}
А у виклику конструктора достатньо вказати аргументи через кому:
Square sq = new Square(2, 2, 2, 2);
Інтерфейси
Java не підтримує множинне успадкування — не можна успадкувати декілька класів, тому що інакше могла б виникнути “ромбовидна проблема” (додатковий матеріал), коли виникає необднозначність, якщо декілька батьківських класів мають метод або поле з однаковим ім’ям. Однак, замість цього є інтерфейси, вони виконують роль повністю абстрактних класів (таких, що мають лише абстрактні методи). У них не має сенсу писати біля кожного метода abstract
, а достатньо створити інтерфейс словом interface
:
interface Animal {
public void voice();
public void move();
}
Замість extends
(розширення класу), інтерфейси implements
(імплементують). Все інше просто, як і з абстрактними класами, потрібно перезаписати всі методи інтерфейсу. Як сказано вище, класи можуть імплементувати більше одного інтерфейсу:
class Cat implements Animal {
public void voice(){
System.out.println("Няв");
}
public void move(){
System.out.println("Кіт іде.");
}
}
Поліморфізм
У розділі про абстракцію були фігури, які наслідують один клас. Цікаво, що можна створити об’єкт конкретний об’єкт, але з типом загального:
Shape a = new Square(3, 3, 3, 3);
Shape b = new Triangle(5, 5, 5);
Таке явище й називають поліморфізмом — одна Фігура має кілька форм: Квадрат і Трикутник. Знаючи, що вони всі фігури (Shape
), можна робити з ними спільні дії, наприклад різні фігури помістити в один масив:
Shape[] arr = {a, b};
Приведення типів
Приведенням типів (кастингом) називають приведення об’єкта до іншого типу, і робиться це вказуванням типу у дужках перед об’єктом:
Square sq = new Square(6, 6, 6, 6);
Shape sh = (Shape)sq;
Приведення до дочірнього типу називається понижувальним приведенням (downcasting), а до батьківського навпаки, повищувальним приведенням (upcasting).
final
У Java відсутнє поняття чистих “констант”, але є слово final
. Клас, який позначений фінальним, не може мати підкласів (його не можна наслідувати); фінальні методи не можна перезаписувати; а фінальні змінні є практично константами, тобто їх не можна змінювати. За конвенцією імен константи у Java називають ВЕЛИКИМИ_ЛІТЕРАМИ:
final class Uninheritable {
public static final float CHYSLO_PI = 3.1415926;
public final example(){}
}