Programowanie średniozaawansowane #10: interfejsy

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ą. 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).

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ładowe implementacje klas, 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
  • klasy implementują (a nie są rozszerzane przez) interfejsy
  • 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

 

Dodaj komentarz