– Cześć, Amigo! Dzisiaj dokonasz wielu odkryć. Temat na dziś — werble — to interfejsy.

– Jasne. Zapowiada się tak ciekawie, że lecę do domu wziąć kąpiel.

Interfejs jest potomkiem Abstrakcji i Polimorfizmu. Interfejs jest bardzo podobny do klasy abstrakcyjnej, w której wszystkie metody także są abstrakcyjne. Jest deklarowany w ten sam sposób co klasa, ale wraz ze słowem kluczowym interface. Oto kilka przykładów:

Kod Opis i fakty
interface Drawable
{
void draw();
}
interface HasValue
{
int getValue();
}
1) Zamiast słowa class piszemy interface.

2) Zawiera on tylko metody abstrakcyjne (nie trzeba dodawać słowa abstract).

3) W rzeczywistości wszystkie metody interfejsów są publiczne.

interface Element extends Drawable, HasValue
{
int getX();
int getY();
}
Interfejs może dziedziczyć tylko inne interfejsy.

Można mieć wiele macierzystych interfejsów.

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

}
Klasa może dziedziczyć wiele interfejsów (i tylko jedną klasę). Aby pokazać to dziedziczenie, używamy słowa kluczowego implements.

Klasa ChessItem  została zadeklarowana jako abstrakcyjna: implementuje ona wszystkie dziedziczone metody oprócz metody draw.

Innymi słowy, ChessItem zawiera jedną metodę abstrakcyjną: draw().

– Interesujące. Po co są nam te interfejsy? Kiedy się ich używa?

– Interfejsy mają przewagę nad klasami w dwóch ważnych aspektach.

1) Rozdzielenie „definicji metod” od ich implementacji.

Poprzednio powiedziałem Ci, że jeśli chcesz pozwolić innym klasom wywoływać metody Twojej klasy, to musisz je oznaczyć jako public. Jeśli chcesz, aby pewne metody były wywoływane tylko z Twojej własnej klasy, to muszą one być oznaczone jako private. Innymi słowy, dzielimy metody klasy na dwie kategorie: «dla wszystkich» i «tylko dla mnie».

Za pomocą interfejsów jeszcze bardziej wzmacniamy ten podział. Możemy utworzyć specjalną „klasę dla wszystkich”, która będzie dziedziczyć drugą „klasę tylko-dla-mnie”. Wyglądałoby to tak:

Przedtem
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;
 }
Potem
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;
 }
}
Przedtem
public static void main(String[] args)
{
 Student student =
               new Student("Alibaba");
 System.out.println(student.getName());
}
Potem
public static void main(String[] args)
{
 Student student =
               new StudentImpl("Ali");
 System.out.println(student.getName());
}

Rozbijamy naszą klasę na dwie części: interfejs i klasę, która ten interfejs implementuje.

– To jakie ma to zalety?

– Ten sam interfejs może być implementowany (dziedziczony) przez różne klasy. A każda klasa może mieć swoje własne zachowanie. Tak, jak ArrayList i LinkedList są dwiema różnymi implementacjami interfejsu List.

Zatem, ukrywamy nie tylko różne implementacje, ale także klasy zawierające implementacje (możemy po prostu użyć interfejsów w każdym miejscu kodu). To pozwala nam w elastyczny sposób, w trakcie działania programu, zastąpić pewne obiekty innymi, zmieniając zachowanie obiektu bez wiedzy klas, które go używają.

W połączeniu z polimorfizmem stanowi to bardzo potężne narzędzie. W tej chwili jeszcze może nie zrozumiesz, do czego jest to potrzebne. Musisz najpierw natknąć się na program składający się z dziesiątek bądź setek klas, aby docenić to, jak bardzo interfejsy mogą Ci ułatwić życie.

2) Wielokrotne dziedziczenie.

W Javie każda klasa ma tylko jedną klasę macierzystą. W innych językach programowania klasy mogą często mieć wiele klas macierzystych. To bardzo wygodne, ale niestety tworzy też swoje problemy.

Java oferuje nam kompromis:  nie można dziedziczyć wielu klas, ale można implementować wiele interfejsów. Interfejs może mieć wiele interfejsów macierzystych. Klasa może implementować wiele interfejsów i dziedziczyć tylko jedną klasę macierzystą.