1. Представяне на интерфейси

Днес е вашият ден за знания. Друга нова и интересна тема са интерфейсите.

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

interface Feline
{
   void purr();
   void meow();
   void growl();
}

Ето някои полезни факти за интерфейсите:

1. Деклариране на интерфейс

interface Drawable
{
   void draw();
}

interface HasValue
{
   int getValue();
}
  1. Вместо ключовата classдума пишем interface.
  2. Съдържа само абстрактни методи (не пишете abstractключовата дума)
  3. Всъщност интерфейсите имат всичкиpublic методи
2. Наследяване на интерфейса

Един интерфейс може да наследява само интерфейси. Но един интерфейс може да има много родители. Друг начин да се каже това е да се каже, че Java има множествено наследяване на интерфейси. Примери:

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

3. Наследяване на класове от интерфейси

Един клас може да наследява множество интерфейси (само от един клас). Това става с помощта на implementsключовата дума. Пример:

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;
   }
}

Класът ChessItem е деклариран абстрактен: той имплементира всички наследени методи с изключение на draw. С други думи, ChessItemкласът съдържа един абстрактен метод — draw().

Техническото meaning на ключовите думи extendsи implementsе същото: и двете са наследство. Разграничението е напequalsо, за да се подобри четливостта на codeа. Казваме също, че класовете се наследяват (чрез extends) и интерфейсите се изпълняват (чрез implements)

4. Променливи

Ето най-важното: обикновените променливи не могат да бъдат декларирани в интерфейси (въпреки че статичните могат).

Но защо се нуждаем от интерфейси? Кога се използват? Интерфейсите имат две силни предимства пред класовете:



2. Разделяне на "описанието на методите" от тяхното изпълнение.

По-рано казахме, че ако искате да разрешите методите на вашия клас да бъдат извиквани от други класове, тогава вашите методи трябва да бъдат маркирани с ключовата publicдума. Ако искате някои от тези методи да се извикват само от вашия клас, трябва да ги маркирате с ключовата privateдума. С други думи, ние разделяме методите на класа на две категории: "за всички" и "само за наша собствена употреба".

Интерфейсите спомагат за по-нататъшното укрепване на това разделение. Ще направим специален „клас за всеки да използва“, Howто и втори клас „само за наша собствена употреба“, който ще наследи първия клас. Ето How би изглеждало приблизително:

Преди След
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());
}

Разделяме нашия клас на две: интерфейс и клас , който наследява интерфейса . И Howво е предимството тук?

Много различни класове могат да имплементират (наследяват) един и същ интерфейс. И всеки може да има собствено поведение. Например ArrayList LinkedListима две различни реализации на Listинтерфейса.

Така скриваме не само различните имплементации, но и самия имплементиращ клас (тъй като имаме нужда само от интерфейса в codeа). Това ни позволява да бъдем много гъвкави: докато програмата се изпълнява, можем да заменим един обект с друг, променяйки поведението на обект, без да засягаме всички класове, които го използват.

Това е много мощна техника, когато се комбинира с полиморфизъм. Засега далеч не е очевидно защо трябва да правите това. Първо трябва да се сблъскате с програми с десетки or стотици класове, за да разберете, че интерфейсите могат да направят живота ви много по-лесен, отколкото без тях.


3. Множествено наследяване

В Java всички класове могат да имат само един родителски клас. В други езици за програмиране класовете често могат да имат множество родителски класове. Това е много удобно, но носи и много проблеми.

Създателите на Java стигнаха до компромис: те забраниха множественото наследяване на класове, но позволиха множественото наследяване на интерфейси. Един интерфейс може да има множество родителски интерфейси. Един клас може да има множество родителски интерфейси, но само един родителски клас.

Защо забраниха множественото наследяване на класове, но позволиха множествено наследяване на интерфейси? Поради така наречения проблем с наследяването на диаманти:

Множествено наследяване

Когато клас B наследи клас A, той не знае нищо за класовете C и D. Така че използва променливите от клас А, Howто намери за добре. Класът 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, тогава ще получим счупен code. Този клас ще има методите на своите предшественици, но те няма да работят правилно, защото имат същите value variable.

Но тъй като интерфейсите не могат да имат променливи, те не могат да имат този вид конфликт. Съответно се допуска множествено наследяване на интерфейси.