1. Способности

За да разберем по-добре предимствата на интерфейсите и къде да ги използваме, трябва да говорим за някои по-абстрактни неща.

Един клас обикновено моделира определен обект. Интерфейсът съответства по-малко на обектите и повече на техните способности or роли.

Същността на интерфейсите

Например, неща като автомобor, велосипеди, мотоциклети и колела са най-добре представени като класове и обекти. Но техните способности - като "Мога да бъда язден", "Мога да транспортирам хора", "Мога да стоя" - са по-добре представени като интерфейси. Ето няколко примера:

Код Описание
interface CanMove
{
   void move(String newLocation);
}
Съответства на способността за движение
interface Rideable
{
   void ride(Passenger passenger);
}
Съответства на способността да бъде язден
interface CanTransport
{
   void addStuff(Object stuff);
   Object removeStuff();
}
Съответства на способността за транспортиране на неща
class Wheel implements CanMove
{
   ...
}
Класът Wheelможе да се движи
class Car implements CanMove, Rideable, CanTransport
{
   ...
}
Класът Carможе да се движи, да бъде язден и да транспортира неща
class Skateboard implements CanMove, Rideable
{
   ...
}
Класът Skateboardможе да се движи и да бъде язден


2. Роли

Интерфейсите значително опростяват живота на програмиста. Много често една програма има хиляди обекти, стотици класове, но само няколко дузини интерфейси , т.е. роли . Има малко роли, но има много начини да ги комбинирате (класове).

Целият смисъл е, че не е нужно да пишете code във всеки клас, за да взаимодействате с всеки друг клас. Просто трябва да взаимодействате с техните роли (интерфейси).

Представете си, че сте дресьор на домашни любимци. Всеки от домашните любимци, с които работите, може да има няколко различни способности. Влизате в приятелски спор със съседа си относно това чии домашни любимци могат да вдигат най-много шум. За да разрешите въпроса, просто подреждате всички домашни любимци, които могат да "говорят", и им давате команда: Говорете!

Не ви интересува Howъв вид животно са or Howви други способности имат. Дори да могат да направят тройно салто назад. В този конкретен момент ви интересува само способността им да говорят високо. Ето How ще изглежда в codeа:

Код Описание
interface CanSpeak
{
   void speak();
}
Способността CanSpeak. Този интерфейс разбира командата за speak, което означава, че има съответен метод.
class Cat implements CanSpeak
{
   void speak()
   {
      println("MEOW");
   }
}

class Dog implements CanSpeak
{
   void speak()
   {
      println("WOOF");
   }
}

class Fish
{
   ...
}
Животни, които имат тази функция.

За да улесним разбирането, предоставихме имената на класовете на английски. Това е разрешено в Java, но е крайно нежелателно.













Нашият Fishняма способността да говори (не реализира интерфейса CanSpeak).

public static void main(String[] args)
{
   // Add all the animals to the list
   ArrayList pets = new ArrayList();
   pets.add(new Cat());
   pets.add(new Dog());
   pets.add(new Fish());

   // If the ability exists, then make a sound
   for(Object pet: pets)
   {
      if (pet instanceof CanSpeak)
      {
         CanSpeak loudmouth = (CanSpeak) pet;
         loudmouth.speak();
      }
   }
}
И How да им дадем команда?

Когато броят на класовете във вашите програми достигне хиляди, вие няма да можете да живеете без интерфейси. Вместо да се описва взаимодействието на хиляди класове, достатъчно е да се опише взаимодействието на няколко десетки интерфейса - това значително опростява живота.

И когато се комбинира с полиморфизъм, този подход като цяло е смазващ успех.



3. defaultРеализация на интерфейсни методи

Абстрактните класове могат да имат променливи и реализации на методи, но не могат да имат множествено наследяване. Интерфейсите не могат да имат променливи or реализации на методи, но могат да имат множествено наследяване.

Ситуацията е представена в следната table:

Способност/свойство Абстрактни класове Интерфейси
Променливи
Изпълнение на метода
Множествено наследяване

И така, някои програмисти наистина искаха интерфейсите да имат способността да имат имплементации на методи. Но наличието на възможност за добавяне на реализация на метод не означава, че такава винаги ще бъде добавена. Добавете го, ако искате. Или ако не го правите, тогава недейте.

В допълнение, проблемите с множественото наследяване се дължат предимно на променливи. Във всеки случай те така решиха и направиха. Започвайки с JDK 8, Java въведе възможността за добавяне на реализации на методи към интерфейсите.

Ето актуализирана table (за JDK 8 и по-нова):

Способност/свойство Абстрактни класове Интерфейси
Променливи
Изпълнение на метода
Множествено наследяване

Сега за абстрактни класове, Howто и за интерфейси, можете да декларирате методи със or без реализация. И това е отлична новина!

В абстрактните класове методите без имплементация трябва да бъдат предшествани от abstractключовата дума. Не е необходимо да добавяте нищо преди методи с имплементация. При интерфейсите е точно обратното. Ако методът няма имплементация, тогава нищо не трябва да се добавя. Но ако има имплементация, тогава defaultтрябва да се добави ключовата дума.

За простота представяме тази информация в следната малка table:

Способност/свойство Абстрактни класове Интерфейси
Методи без реализация abstract
Методи с реализация default

проблем

Използването на интерфейси, които имат методи, може значително да опрости големи йерархии на класове. Например, абстрактното InputStreamи OutputStreamкласовете могат да бъдат декларирани като интерфейси! Това ни позволява да ги използваме много по-често и много по-удобно.

Но вече има десетки мorони (мorарди?) Java класове в света. И ако започнете да променяте стандартните библиотеки, тогава може да счупите нещо. Като всичко! 😛

За да не се разрушат случайно съществуващи програми и библиотеки, беше решено внедряването на метод в интерфейсите да има най-нисък приоритет на наследяване .

Например, ако един интерфейс наследява друг интерфейс, който има метод, и първият интерфейс декларира същия метод, но без имплементация, тогава имплементацията на метода от наследения интерфейс няма да достигне до наследяващия интерфейс. Пример:

interface Pet
{
   default void meow()
   {
      System.out.println("Meow");
   }
}

interface Cat extends Pet
{
   void meow(); // Here we override the default implementation by omitting an implementation
}

class Tom implements Cat
{
}

Кодът няма да се компorра, защото Tomкласът не имплементира meow()метода.