Zespół Szkół nr 7 w Wałbrzychu

Interfejsy zostały stworzone dla klas o podobnych właściwościach czy zachowaniach ale nie powiązanych ze sobą (dziedziczenie) posiadających za to pewne wspólne cechy (wymagane jest, by je posiadały). Każda klasa, która implementuje interfejs musi implementować wszystkie jego metody.

Klasy abstrakcyjne pozwalają na określenie czegoś w rodzaju zarysu klasy, który przyjmuje swoją ostateczną postać dopiero w klasie potomnej.
Struktura interfejsu przypomina strukturę klasy, ale nie zawiera zmiennych, które mogą zmieniać wartość (choć może zawierać stałe z przypisaną wartością) i zwykle nie posiada implementacji zadeklarowanych w niej metod.

O interfejsach należy myśleć jako o całkowicie abstrakcyjnych klasach. Interfejs może mieć metody, ale tylko abstrakcyjne, czyli nie może zawierać ich implementacji. Klasy abstrakcyjne mogą zawierać zarówno metody abstrakcyjne (bez implementacji), jak i zwykłe metody (z implementacją w klasie abstrakcyjnej).

Sens tworzenia interfejsów jest taki, że pozwalają one na opisanie tego jaką funkcjonalność powinny mieć klasy, ale nie jak ją mają realizować. Do podstawowych różnic w porównaniu do klas abstrakcyjnych i ogólniej mechanizmu dziedziczenia jest to, że o ile klasa pochodna może dziedziczyć bezpośrednio tylko po jednej klasie, to klasa może implementować na raz kilka interfejsów.

Właściwości interfejsów:

  • Wszystkie metody interfejsu są domyślnie publiczne i abstrakcyjne, nie trzeba tego zaznaczać przy deklaracji.
  • Wszystkie pola interfejsu są domyślnie publiczne, statyczne i finalne (też nie trzeba tego deklarować). Wszystkie klasy, które implementują interfejs, będą miały zawsze bezpośredni dostęp do tych samych (stałych) wartości.
  • Metody interfejsów nie mogą być statyczne (w javie 8 już mogą), nie mogą być oznaczone jako final, strictfp, native (finalnych metod nie można nadpisać w klasie implementującej interfejs). Od javy 8 pojawiają się metody domyślne (z modyfikatorem default), które pozwalają zdefiniować ciało metody w interfejsie. Co więcej, zachowujemy możliwość wielokrotnego dziedziczenia!
  • Interfejsy mogą rozszerzać jedynie inne interfejsy.
  • Interfejs nie może implementować innego interfejsu.
  • Deklaracja interfejsu odbywa się przy pomocy słowa kluczowego „interface”.

Implementacje metod z interfejsu, nie mogą deklarować nowych, ani modyfikować już zadeklarowanych wyjątków. W implementowanej metodzie nie jest wymagane, aby powtórnie deklarować wyjątek.

Np.:

public interface Zwierzak {
   String jedz( );
   Object rozmnażaj_Się( );
   void rośnij(double wzrostMasy);
}

Jak widać, są to same deklaracje określające nazwę metody, rodzaj wartości zwracanej przez metodę oraz argumenty, które przyjmuje.
Jeśli klasa implementuje interfejs to znaczy, że zgadza się na swoisty kontrakt, że zaimplementuje wszystkie metody w niej zawarte.

public class Mysz implements Zwierzak {
…tu muszą być stworzone (zaimplementowane) metody:
jedz( ), rozmnażaj_Się( ), rośnij( )
… inne elementy klasy, np. metoda jedz_ser( );
}

Teraz obiekt:
Zwierzak mysiok = new Mysz( );
jest typowym zwierzakiem (bo: je, rozmnaża się, rośnie) i jego opis i  zachowania jest typowe dla wszystkich zwierzaków (deklarowanych w interfejsie Zwierzak)
Mysz mysiok = new Mysz( );
jest myszą „pełną gębą” i prócz wspólnych cech zwierzaków potrafi jeszcze jeść_ser

jeśli teraz w głównym programie stworzymy metodę:
nakarmZwierzaka(Zwierzak z) {   z.jedz(); }

to przyjmuje ona jako argument każdy obiekt, który został utworzony na podstawie klasy implementującej interfejs Zwierzak.
Wykorzystuje metody, które są w tym interfejsie zadeklarowane i nie musi “wiedzieć” jakiego dokładnie typu są te obiekty, w których implementacja tych metod a także wynik ich działania może być zupełnie inny.

Jeśli mamy interfejs pracownik, w którym firma określiła kontrakt, że by zostać pracownikiem firmy trzeba opisać swoją metodę pracy w określony sposób podając w wyniku zapłatę to: jeśli młynarz chce być pracownikiem musi tę metodę opisać wg tego schematu. Przy pewności, że każdy rodzaj pracownika określił zapłatę (a braku zainteresowania w szczegółach jak pracuje) – możemy przy określonym budżecie i wymaganiach co do rodzaju pracownika zaplanować wykonanie jakiejś pracy.

Wraz z Javą w wersji 8 pojawiła się możliwość umieszczania w interfejsach także implementacji metod. W takich wypadkach są to tzw. “metody domyślne”. Metoda domyślna musi otrzymać modyfikator default.

Umieszczenie domyślnej implementacji nowych metod rozwiązuje problem wstecznej kompabitylności języka java, stare klasy w programach nie muszą ich implementować.
Drugą zaletą stosowania metod domyślnych jest to, że mogą one być tworzone wtedy, gdy przewidujemy, że implementujące interfejs metody mogą korzystać tylko z części funkcjonalności oferowanej przez interfejs. W takim przypadku programista nie jest zmuszony do implementowania metod, których i tak nie będzie wykorzystywać.

Jeśli teraz do interfejsu Zwierzak dopiszemy domyślną metodę:
default void kopNore() {
System.out.println(„Kopię…”);
}

to nie ma sensu jej implementować dla Słonia.

DZIEDZICZENIE METOD DOMYŚLNYCH
public interface A { default int foo(Integer n) { return n + n; } }
public interface B { default int foo(Integer n) { return n + 2;} }

public class C implements A,B {
@Override
public int foo(Integer n) {
return A.super.foo(n);
}
}

METODY STATYCZNE W INTERFEJSACH

public interface InterfaceWithStaticMethod {
public static String itWorks(int number) {
return String.format(„Works for %d”, number);
}
}
Zastosowanie: implementacja metod statycznych w interfejsie pozwoli zrezygnować z tzw. klas narzędziowych (przykład: java.util.Collections dla interfejsu java.util.Collection<E>

materiały:
http://ggoralski.pl