Výchozí a statické metody rozhraní

  • Java 8 přidává ohledně metod v rozhraní nové možnosti.

  • Neuvidíme je tedy ve starém kódu a mnozí vývojáři je neznají.

  • Jde o možnosti, jak již do rozhraní implementovat určitou základní funkčnost; nenechávat to až na třídy.

  • Jsou dva základní typy těchto metod:

    • výchozí (default)

    • statické (static)

Výchozí metody rozhraní

  • V originálu default methods

  • Přidávají možnost implementovat přímo v rozhraní i určitou funkcionalitu.

  • Hlavním smyslem je, aby se při vývoji rozhraní, tj. přidávání metod do něj, nenarušil stávající kód implementující toto rozhraní

  • Metoda nově přidaná do rozhraní se tudíž hned v něm musí implementovat.

  • Tuto funkcionalitu je možné samozřejmě implementovat jen na základě volání (dosud) abstraktních

  • nebo statických (viz dále) metod tohoto rozhraní.

  • V definici rozhraní jsou výchozí metody uvozeny klíčovým slovem default a obsahují rovněž tělo (tj. implementaci) metody.

Příklad

zdroj: Java SE 8’s New Language Features, Part 1: Interface Default/Static Methods and Lambda Expressions

public interface Addressable {
   String getStreet();
   String getCity();
   default String getFullAddress() {
      return getStreet() +", " + getCity();
   }
}

Významná použití výchozích metod

  • Výchozí metody se mohou zdát zbytečností.

  • Ale je několik situací, kdy se velmi hodí.

    Vývoj existujících rozhraní

    Dříve, bez výchozích metod, nebylo možné přidat v budoucí verzi rozhraní do něj metodu, aniž by se porušila binární kompatibilita se stávajícím kódem — všechny třídy implementující toto rozhraní by musely počínaje od nové verze implementovat i novou přidanou metodu, tudíž by stávající kód přestal fungovat.

    Zvýšení pružnosti návrhu

    Výchozí metody nás zbavují časté nutnosti použít abstraktní třídu pro implementaci obecných metod, které by se jinak ve (často všech) implementujících neabstraktních třídách opakovaly. Abstraktní třída pak návrháře nutí de facto nikoli k pouhé implementaci rozhraní, ale k dědění z takové abstraktní třídy, což narušuje javový koncept preferující implementaci rozhraní před dědičností.

Statické metody

  • Rozhraní může počínaje Java 8 obsahovat statické metody.

  • Ty se píší i chovají stejně jako ve třídě, tj. nesmějí pracovat s atributy a metodami objektu, nýbrž jen celé třídy = dalšími statickými.

  • Statické metody se mohou v rozhraní využít při psaní výchozích metod.

Příklad statické metody

interface A {
   static void someStaticMethod() {
      /* some stuff */
   }
   default void someMethod() {
      // může zavolat statickou metodu
      someStaticMethod();
   }
}

Rozšiřování rozhraní s výchozí metodou

  • Mějme rozhraní A obsahující nějakou výchozí metodu, třeba dm().

  • Definujeme-li nyní rozhraní B jako rozšíření (extends) rozhraní A, mohou nastat tři různé situace:

    1. Jestliže výchozí metodu dm() v rozhraní B nezmiňujeme, pak se podědí z A.

    2. V rozhraní B uvedeme metodu dm(), ale jen její hlavičku (ne tělo). Pak ji nepodědíme, stane se abstraktní jako u každé obyčejné metody v rozhraní a každá třída implementující rozhraní B ji musí sama implementovat.

    3. V rozhraní B implementujeme metodu znovu, čímž se původní výchozí metoda překryje — jako při dědění mezi třídami.

Více výchozích metod — chybně

  • Následující kód Java 8 (a samozřejmě ani žádná starší) nezkompiluje.

    interface A {
       default void someMethod() { /*bla bla*/ }
    }
    interface B {
       default void someMethod() { /*bla bla*/ }
    }
    class C implements A, B {
       // překladač by nevěděl, kterou someMethod() použít
    }

Více výchozích metod — překryté, OK

  • Následující kód Java 8 (ale samozřejmě žádná starší) bez potíží zkompiluje.

    interface A {
       default void someMethod() { /*bla bla*/ }
    }
    interface B {
       default void someMethod() { /*bla bla*/ }
    }
    class D implements A, B {
       // překryjeme-li (dvojitě) poděděnou metodu, není problém
       // překladač nemusí přemýšlet, someMethod() použít
       public void someMethod() {
          // the right stuff, this will be used
       }
    }

Jedna metoda výchozí, druhá abstraktní

  • Následující kód Java 8 opět nezkompiluje.

  • Jedno rozhraní default metodu má a druhé ne.

    interface A { void someMethod(); }
    interface B { default void someMethod() { /* whatever */ } }
    class E implements A, B {
       // nepřeloží, protože zůstává otázka:
       // má či nemá překladač použít výchozí metodu?
    }

Dokumentace