— Привіт! Сьогодні маєш день відкриттів. Нова та цікава тема – це інтерфейси.

— Ага. День настільки чудовий, що я прийду додому і прийму повну ванну води.

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

Код Опис та Факти
interface Drawable
{
void draw();
}
interface HasValue
{
int getValue();
}
1) Замість слова class пишемо interface.

2) Містить лише абстрактні методи (слово abstract писати не потрібно).

3) Насправді в інтерфейсів усі методи — public.

interface Element extends Drawable, HasValue
{
int getX();
int getY();
}
Інтерфейс може успадковуватись лише від інтерфейсів.

Інтерфейсів-батьків може бути багато.

abstract class ChessItem implements Drawable, HasValue
{
private int x, y, value;

public int getValue()
{
return value;
}

public int getX()
{
return x;
}

public int getY()
{
return y;
}

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

Клас ChessItem оголошено абстрактним: він реалізував усі успадковані методи, крім draw.

Тобто клас ChessItem містить один абстрактний метод: draw().

— Цікаво. А навіщо потрібні інтерфейси? Коли їх використовують?

— Інтерфейси мають дві сильні переваги в порівнянні з класами:

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

Раніше я тобі розповідав, що  якщо ти хочеш дозволити викликати методи свого класу з інших класів, їх потрібно позначити ключовим словом 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.

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

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

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

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

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