Інтерфейс у мові програмування Java — це абстрактний тип, який використовується для визначення поведінки, яку класи повинні реалізовувати. Інтерфейси схожі до протоколів. Інтерфейси оголошуються за допомогою ключового слова interface
, та можуть містити тільки прототипи методів і оголошення констант (або змінних, які оголошені як static
чи final
). Усі методи інтерфейсу не можуть містити реалізації тіл методів в усіх версіях нижче Java 8. Починаючи з Java 8, default
та static
методи можуть мати реалізацію за замовчуванням при визначенні інтерфейсу.[1]
Інтерфейс не може бути інстанційованим, а тільки реалізованим (тобто не можна створити об'єкт інтерфейсу з використанням оператора new). Клас, що реалізує інтерфейс, повинен визначити усі методи, описані в цьому інтерфейсі, або має бути абстрактним класом. Посилання на об'єкти в Java можуть бути позначені типом інтерфейсу; тоді у кожному такому випадку посилання може бути або null, або вказувати на об'єкт, що реалізує інтерфейс.
Однією з переваг використання інтерфейсів є те, що вони імітують множинне успадкування. Усі класи Java повинні мати тільки один базовий клас, єдиним винятком з цього правила є java.lang.Object
(верхній тип системи типів Java), оскільки множинне успадкування класів не допускається. Однак, інтерфейс може успадковувати декілька інтерфейсів, так як і клас може реалізовувати декілька інтерфейсів.
Інтерфейси використовуються для описання подібності, яку поділяють різні класи, але які не обов'язково мають класові взаємозв'язки. Наприклад, людина і папуга обидва можуть свистіти; однак, не має сенсу представляти Human
і Parrot
класи як підкласи Whistler
. Швидше за все, вони будуть підкласами класу Animal
(ймовірно, з проміжними класами), але обидва будуть реалізовувати інтерфейс Whistler
.
Інше використання інтерфейсів — можливість використовувати об'єкт, фактично без наявності інформації про його тип, але про який відомо, що він реалізує певний інтерфейс. Виклик whistler.whistle()
буде викликати реалізацію whistle
метода об'єкта whistler
незалежно від того, який конкретний тип має цей об'єкт, але тільки за умови, що він реалізує Whistler
. Одним з практичних прикладів є алгоритм сортування, який на вхід очікує об'єкти типу Comparable
. Таким чином, без знання конкретного типу, узагальнений код знає, що об'єкти таких типів можна якимось чином порівняти, отже, і відсортувати.
Наприклад:
interface Bounceable {
double pi=3.1415;
void setBounce(); // Методи інтерфейсів публічні, абстрактні і ніколи не бувають незмінними (final).
// Думайте про них тільки як про прототипи; їх реалізація не дозволена.
}
Інтерфейс:
Інтерфейси визначаються наступним синтаксисом (порівняйте з визначенням класу Java):
[видимість] interface InterfaceName [успадкування інших інтерфейсів] {
константні оголошення
абстрактні оголошення методів
}
Тіло інтерфейсу містить абстрактні методи, та оскільки всі методи в інтерфейсі є, за визначенням, абстрактними, ключове слово abstract
не потрібно. Так як інтерфейс вказує на відкриту поведінку типу, всі методи неявно є public
методами.
Таким чином, простий інтерфейс може бути визначений як
public interface Predator {
boolean chasePrey(Prey p);
void eatPrey(Prey p);
}
Оголошення внутрішнього класу всередині тіла інтерфейсу неявно є static
, final
і public
.[2]
Синтаксис реалізації інтерфейсу використовує наступне визначення:
... implements InterfaceName[, інший інтерфейс, ще один, ...] ...
Наприклад,
public class Lion implements Predator {
@Override
public boolean chasePrey(Prey p) {
// програма переслідування здобичі (спеціалізація левів).
}
@Override
public void eatPrey(Prey p) {
// програма поїдання здобичі (спеціалізація левів)
}
}
Якщо клас реалізує інтерфейс, але не реалізує всі його методи, він повинен бути позначений як abstract
. Якщо клас є абстрактним, очікується, що один з його підкласів реалізує ці нереалізовані методи, або, якщо будь-який з підкласів абстрактного класу не реалізує всі методи інтерфейсу, цей підклас повинен бути знову позначений міткою abstract
. Класи можуть реалізовувати кілька інтерфейсів:
public class Frog implements Predator, Prey { ... }
Інтерфейси можуть мати спільні методи класу:
class Animal implements LikesFood, LikesWater {
boolean likes() {return true;}
}
Однак даний клас не може реалізовувати той самий або аналогічний інтерфейс декілька разів:
class Animal implements Shares<Boolean>, Shares<Integer> ...
// помилка: повторення інтерфейсу
Інтерфейси зазвичай використовуються в мові Java для функцій зворотного виклику (callback),[3] оскільки Java не дозволяє багаторазове успадкування класів, і не дозволяє передавати методи (процедури) як аргументи функцій. Тому для того, щоб передати деяку дію як параметр цільовому методу, поточний підхід полягає у створенні класу, що реалізує потрібний інтерфейс, та передачі посилання на нього у цільовий метод. Цей підхід базується на так званій таблиці віртуальних методів, що супроводжує кожен об'єкт, та яка допомагає викликати метод потрібного інтерфейсу об'єкта без фактичного знання його типу.
Інтерфейси можуть розширювати декілька інших інтерфейсів, використовуючи наступний синтаксис:
public interface VenomousPredator extends Predator, Venomous {
//interface body
}
що є дозволеним і визначає надінтерфейс. Зверніть увагу на те, що багаторазове успадкування дозволене, на відміну від класів. Також зауважте, що Predator
і Venomous
можуть визначати або успадкувати методи з однаковими параметрами та порядком типів, скажімо kill(Prey p)
. Коли клас реалізує VenomousPredator
він реалізує обидва методи одночасно.
Деякі інтерфейси стандартної бібліотеки Java:
Comparable
має метод compareTo
, який використовується для опису двох об'єктів як рівних, або для позначення одного більше, ніж для іншого. Узагальнене програмування дозволяє створювати класи, щоб вказати, які екземпляри класу можна порівняти з ними.Serializable
— це інтерфейс-маркер без методів або полів — він має порожнє тіло. Цей інтерфейс використовується для позначення класів, які можна серіалізувати. Його Javadoc сторінка описує, як він повинен функціювати, хоча програмно нічого не нав'язується.