Назад Зміст Вперед

8.3. Реалізація множинного наслідування через інтерфейси

Реалізація множинного наслідування через інтерфейси

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

При спадкуванні базовий клас зовсім не зобов'язаний бути абстрактним або «реальним» (без абстрактних методів). Якщо спадкування дійсно здійснюється не від інтерфейсу, то серед прямих «предків» клас може бути тільки один - всі інші повинні бути інтерфейсами. Імена інтерфейсів перераховуються слідом за ключовим словом implements і розділяються комами. Інтерфейсів може бути скільки завгодно, причому до них можна проводити висхідне перетворення. Наступний приклад показує, як створити новий клас на основі реального класу і декількох інтерфейсів:

// Використання кількох інтерфейсів
 
interface CanFight {
  void fight();
}
 
interface CanSwim {
  void swim();
}
 
interface CanFly {
  void fly();
}
 
class ActionCharacter {
  public void fight() {}
} 
 
class Hero extends ActionCharacter
    implements CanFight, CanSwim, CanFly {
  public void swim() {}
  public void fly() {}
}
 
public class Adventure {
  public static void t(CanFight x) { x.fight(); }
  public static void u(CanSwim x) { x.swim(); }
  public static void v(CanFly x) { x.fly(); }
  public static void w(ActionCharacter x) { x.fight(); }
  public static void main(String[] args) {
    Hero h = new Hero();
    t(h); // Використано об'єкт типу CanFight
    u(h); // Використано об'єкт типу CanSwim     v(h); // Використано об'єкт типу CanFly    w(h); // Використано об'єкт типу ActionCharacter 
  }
}
Клас Неrо поєднує реальний клас Action Character з інтерфейсами CanFight, CanSwim і CanFly. При об'єднанні реального класу з інтерфейсами на першому місці має стояти реальний клас, а за ним слідують інтерфейси (інакше компілятор видасть помилку). 

Зауважте, що оголошення методу fight () в інтерфейсі CanFight збігається з тим, що є в класі ActionCharacter, і тому в класі Неrо немає визначення методу fight (). Інтерфейси можна розширювати, але при цьому виходить інший інтерфейс. Необхідною умовою для створення об'єктів нового типу є наявність всіх визначень. Хоча клас Неrо не має явного визначення методу fight (), це визначення існує в класі ActionCharacter, що і робить можливим створення об'єктів класу Неrо

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

Система інтерфейсів Java спроектована так, що вона нормально працює без особливих зусиль з боку програміста. 
Пам'ятайте, що головна причина введення в мову інтерфейсів представлена ​​в наведеному прикладі: це можливість виконувати висхідне перетворення до декількох базових типів. 
Друга причина для використання інтерфейсів збігається з призначенням абстрактних класів: заборонити програмісту-клієнту створення об'єктів цього класу. 

Виникає природне запитання: що краще - інтерфейс або абстрактний клас? Якщо можна створити базовий клас без визначень методів і змінних-членів, вибирайте саме інтерфейс, а не абстрактний клас. Взагалі кажучи, якщо відомо, що щось буде використовуватися як базовий клас, насамперед постарайтеся зробити це «щось» інтерфейсом. 
.