Об’єктно-орієнтоване програмування (продовження)

Одразу перейдемо до теми.

Успадкування

У вступі до ООП розповідалось про те, що клас може наслідувати інший, розширюючи його. Такі підкласи мають окрім полей і методів суперкласу, ще й свої власні. Для наслідування необхідно після назви класу вказати слово 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(){}
}

Copyleft 🄯 2020–2024 Михайло Стецюк <yaBobJonez@gmail.com>.
Сайт має ліцензію Creative Commons License.
Дякую рідній школі та вчителям!