Implementacja interfejsu w programowaniu
Pewnie już znasz pojęcie interfejs, które często występuje jako interfejs graficzny. Wtedy można go rozumieć, jako sposób w jakim użytkownik komunikuje się z komputerem (z reguły są to różnego rodzaju buttony, linki, combo boxy, itp.). Interfejs (czy też po angielsku: interface) o którym dziś mowa, jest sposobem w jaki jedna klasa powinna komunikować się z drugą. Dzieje się tak dlatego, bo implementacja interfejsu dokonywana jest właśnie w klasie.
Często w podręcznikach mówi się o „kontrakcie” pomiędzy dwoma stronami usług. Wiem, że brzmi to enigmatycznie. W praktyce chodzi po prostu o to, że przygotowujemy sobie coś, co przypomina bardzo klasę czysto abstrakcyjną (czy zawierającą tylko sygnatury metod *). Każda następna klasa, tak jak w przypadku dziedziczenia po klasie abstrakcyjnej, musi zaimplementować wszystkich jej sygnatur.
Po co taka sztuczka? Pamiętasz lekcję o klasa abstrakcyjnych? Musisz wiedzieć, że programowanie w dzisiejszych czasach to sport zespołowy. Dawno już minęły czasy, gdy kod w całości jest pisany przez jednego programistę. Faktycznie wtedy, gdy piszesz kod sam, mógłbyś nie używać żadnych klas abstrakcyjnych czy interfejsów. Jednak ich używanie, ułatwia kolejnym osobom czytelność kodu i Twoje zamierzenia. Wytłumacz to sobie w ten sposób, że interfejsy wskazują kolejnym osobom, co powinny zaimplementować w klasie. No dobrze, ale skoro są klasy abstrakcyjne, to po co te interfejsy, skoro można używać dziedziczenia? Tu również odpowiedź jest dość zaskakująca, otóż interfejsów możesz implementować nieskończoną ilość, a dziedziczyć (jak już wiesz) można tylko po jednej klasie (lub, co ciekawe, po jednym interfejsie).
Implementacja własnych interfejsów
Dla ćwiczenia stwórz sobie dwa interfejsy. Jeden dla bramkarza a drugi dla każdego innego zawodnika.
public interface GoalKeeperBehaviour { public int handle(); }
public interface Skillable { public int pass(); public int attack(); public int defend(); }
Teraz zaimplementuję zachowanie ogólnie (Skillable) dla napastnika (Striker) oraz dla bramkarza (Goalkeeper), a dodatkowo także zachowanie dla bramkarza (GoalkeeperBehaviour). Gdy tylko wpiszesz w swoich nowo utworzonych klasach interfejs, Eclipse na czerwono zaznaczy Ci, że należy zaimplementować wszystkie metody, których sygnatury są w interfejsie.
public class GoalKeeper implements GoalKeeperBehaviour, Skillable { // blad, brak zaimplementowanych wymaganych metod }
Poprawne przykładowa implementacja interfejsu, mogą wyglądać tak:
public class Striker implements Skillable { @Override public int pass() { return 15; } @Override public int attack() { return 17; } @Override public int defend() { return 9; } }
Możesz też implementować więcej niż jeden interfejs
public class GoalKeeper implements GoalKeeperBehaviour, Skillable { @Override public int pass() { return 8; } @Override public int attack() { return 4; } @Override public int defend() { return 6; } @Override public int handle() { return 18; } }
Możesz też rozszerzyć jeden interfejs o drugi:
public interface CaptainBehaviour { public int makeInfluance(); }
public interface Skillable extends CaptainBehaviour, GoalKeeperBehaviour{ public int pass(); public int attack(); public int defend(); }
Co więcej możesz tu rozszerzać (w przeciwieństwie do klas) o więcej niż jeden interfejs.
public class PlayerMain { public static void main(String[] args) { GoalKeeper goalKeeper = new GoalKeeper(); System.out.println(goalKeeper.handle()); Skillable striker = new Striker(); System.out.println(striker.attack()); } }
Teraz możesz wywołać stworzone klasy w metodzie main. Zauważ, że od tej lekcji możesz tworzyć obiekty nie tylko poprzez użycie nazwy klasy w sekcji deklaratywnej (po lewej stronie znaku „równa się”), ale także z użyciem interfejsu (np. Skillable striker = new Striker();). Używanie interfejsów pozwala na bardziej elastyczne pisanie kodu, ale docenisz to dopiero z czasem.
Podsumujmy informacje o interfejsach:
- zawierają dowolną liczbę sygnatur metod (czyli metodę bez jej ciała)
- są widoczne w pakietach tak jak klasy
- nie możesz stworzyć instancji interfejsu (czyli np. Skillable skills = new Skillable(); nie zadziała, podobnie jak w przypadku klasy abstrakcyjnej)
- mogą zawierać stałe (public static final..)
- nie posiadają zmiennych klasowych i konstruktorów
- implementacja interfejsu dokonywana jest w klasie.
- klasy nie mogą być rozszerzane przez interfejsy (ani na odwrót)
- interfejs może być rozszerzone o inne interfejsy
- od Javy 8 doszły nowe zmiany dotyczące interfejsów, które opowiem Ci w oddzielnym kursie, w którym skupię się tylko na zmianach jakie zaszły w tej wersji Javy
Link do lekcji: https://github.com/developeronthego/java-middle/tree/master/src/main/java/middle/lesson10
Powtórka o tym, czy jest polimorfizm: Java #19: polimorfizm i słowo kluczowe this