1. Знайомство з інтерфейсами

Сьогодні у вас справжній день знань. Ще одна нова й цікава тема — інтерфейси.

Інтерфейс — це дитя Абстракції та Поліморфізму. Інтерфейс дуже нагадує абстрактний клас, який має лише абстрактні методи. Він оголошується так само, як і клас, тільки використовується ключове слово interface.

interface Котячі
{
   void мурчати();
   void нявкати();
   void гарчати();
}

Декілька корисних фактів про інтерфейси:

1. Оголошення інтерфейсу

interface Drawable
{
   void draw();
}

interface HasValue
{
   int getValue();
}
  1. Замість слова class пишемо interface.
  2. Містить лише абстрактні методи (слово abstract писати не потрібно)
  3. Усі методи інтерфейсів — public
2. Успадкування інтерфейсів

Інтерфейс може успадковуватися тільки від інтерфейсів. Натомість може мати багато батьків. Про це кажуть, що в Java підтримується множинне успадкування інтерфейсів. Приклади:

interface Element extends Drawable, HasValue
{
   int getX();
   int getY();
}

3. Успадкування класів від інтерфейсів

Клас може успадковуватися від декількох інтерфейсів (але тільки від одного класу). Для цього використовується ключове слово implements. Приклад:

class abstract ChessItem implements Drawable, HasValue
{
   private int x, y, value;
   public int getValue()
   {
      return value;
   }

   public int getX()
   {
      return x;
   }

   public  int getY()
   {
      return y;
   }
}

Клас ChessItem оголошено як абстрактний: він реалізував усі успадковані методи, крім draw. Тобто клас ChessItem містить один абстрактний метод — draw().

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

4. Змінні

І найважливіше: в інтерфейсах не можна оголошувати змінні (проте статичні — можна).

А навіщо ж потрібні інтерфейси? Коли їх використовують? Інтерфейси порівняно з класами мають дві суттєві переваги, які розглянемо далі.



2. Відокремлення «опису методів» від їх реалізації.

Раніше ми вже розповідали, що коли ви хочете дозволити виклик методів свого класу з інших класів, то їх потрібно позначити ключовим словом public. А коли ви хочете, щоб певні методи можна було викликати тільки з цього самого класу, їх потрібно позначити ключовим словом private. Інакше кажучи, ми поділяємо методи класу на дві категорії: «для всіх» і «тільки для своїх».

За допомогою інтерфейсів цей поділ можна підсилити. Ми зробимо один спеціальний «клас для всіх» і другий «клас для своїх», який успадкуємо від першого. Це матиме такий вигляд:

Було Стало
class Student
{
   private String name;
   public Student(String name)
   {
      this.name = name;
   }

   public String getName()
   {
      return this.name;
   }

   private void setName(String name)
   {
      this.name = name;
   
}
interface Student
{
   public String getName();
}

class StudentImpl implements Student
{
   private String name;
   public StudentImpl(String name)
   {
      this.name = name;
   }

   public String getName()
   {
      return this.name;
   }

   private void setName(String name)
   {
      this.name = name;
   }
}
public static void main(String[] args)
{
   Student student = new Student("Alibaba");
   System.out.println(student.getName());
}
public static void main(String[] args)
{
   Student student = new StudentImpl("Ali")
   System.out.println(student.getName());
}

Ми розділили наш клас на дві частини: інтерфейс і клас, успадкований від інтерфейсу. Які ж переваги ми отримали?

Той самий інтерфейс можуть реалізовувати (успадковувати) різні класи, і кожен із них може мати свою поведінку. Так само, як ArrayList і LinkedList — це дві різні реалізації інтерфейсу List.

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

Це дуже потужна технологія в поєднанні з поліморфізмом. Зараз вам, мабуть, зовсім не очевидно, навіщо так робити. Щоб зрозуміти, як інтерфейси істотно спрощують життя, вам спочатку потрібно попрацювати з програмами, що складаються з десятків і сотень класів.


3. Множинне успадкування

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

У Java дійшли компромісу: заборонили множинне успадкування класів, але дозволили множинне успадкування інтерфейсів. Інтерфейс може мати декілька інтерфейсів-батьків. Клас може мати декілька інтерфейсів-батьків, але батьківський клас може бути тільки один.

Чому ж для класів множинне успадкування заборонили, а для інтерфейсів дозволили? Причиною є так зване пірамідальне успадкування:

Множинне успадкування

Коли клас B успадковується від класу A, йому нічого не відомо про класи C і D. Тому він використовує змінні класу A так, як вважає за потрібне. Клас C робить те саме: використовує змінні класу A, але вже в інший спосіб. А в класі D це все призводить до конфлікту.

Розгляньмо простий приклад. Припустімо, у нас є 3 класи:

class Data
{
   protected int value;
}
class XCoordinate extends Data
{
   public void setX (int x) { value = x;}
   public int getX () { return value;}
}
class YCoordinate extends Data
{
   public void setY (int y) { value = y;}
   public int getY () { return value;}
}

Клас Data зберігає змінну value. Його клас-спадкоємець XCoordinate використовує її для зберігання змінної X, а клас-спадкоємець YCoordinate — для зберігання змінної Y.

І це працює — окремо. Але якщо ми захочемо успадкувати клас XYCoordinates від обох класів — XCoordinate і YCoordinate — отримаємо непрацюючий код. У цього класу будуть методи його класів-предків, але працювати вони будуть неправильно, тому що змінна value в них одна.

А оскільки інтерфейсам заборонено мати змінні, то вони не матимуть таких конфліктів. Тому множинне успадкування інтерфейсів дозволено.