Szukaj…


Wprowadzenie

Metoda domyślna wprowadzona w Javie 8 pozwala programistom dodawać nowe metody do interfejsu bez przerywania istniejących implementacji tego interfejsu. Zapewnia elastyczność pozwalającą interfejsowi zdefiniować implementację, która będzie używana domyślnie, gdy klasa implementująca ten interfejs nie zapewni implementacji tej metody.

Składnia

  • public default void methodName () {/ * body method * /}

Uwagi

Metody domyślne

  • Może być używany w interfejsie w celu wprowadzenia zachowania bez zmuszania istniejących podklas do jego wdrożenia.
  • Może być zastąpiony przez podklasy lub pod-interfejs.
  • Nie wolno zastępować metod w klasie java.lang.Object.
  • Jeśli klasa implementująca więcej niż jeden interfejs dziedziczy metody domyślne z identycznymi sygnaturami metod z każdego interfejsu, wówczas musi zastąpić i udostępnić własny interfejs, tak jakby nie były to metody domyślne (w ramach rozwiązywania wielokrotnego dziedziczenia).
  • Chociaż mają na celu wprowadzenie zachowania bez przerywania istniejących implementacji, istniejące podklasy z metodą statyczną z taką samą sygnaturą metody, jak nowo wprowadzona metoda domyślna, nadal będą zepsute. Jest to jednak prawdą nawet w przypadku wprowadzenia metody instancji do nadklasy.



Metody statyczne

  • Może być używany w interfejsie, przeznaczony głównie do stosowania jako metoda narzędziowa dla metod domyślnych.
  • Nie można go przesłonić podklasami ani pod-interfejsem (jest dla nich ukryty). Jednak tak jak w przypadku metod statycznych nawet teraz, każda klasa lub interfejs może mieć swoją własną.
  • Nie wolno przesłonić metod instancji w klasie java.lang.Object (tak jak ma to obecnie miejsce również w przypadku podklas).



Poniżej znajduje się tabela podsumowująca interakcje między podklasą i superklasą.

- SUPER_CLASS-INSTANCE-METHOD SUPER_CLASS-STATIC-METHOD
METODA SUB_CLASS-INSTANCE zastępuje generuje błąd podczas kompilacji
SUB_CLASS-STATIC-METHOD generuje błąd podczas kompilacji chowa się



Poniżej znajduje się tabela podsumowująca interakcję między interfejsem a klasą implementacyjną.

- DOMYŚLNA METODA INTERFEJSU METODA INTERFEJS-STATYCZNA
IMPL_CLASS-INSTANCE-METHOD zastępuje chowa się
IMPL_CLASS-STATIC-METHOD generuje błąd podczas kompilacji chowa się

Bibliografia :

  • http://www.journaldev.com/2752/java-8-interface-changes-static-method-default-method
  • https://docs.oracle.com/javase/tutorial/java/IandI/override.html

Podstawowe użycie metod domyślnych

/**
 * Interface with default method
 */
public interface Printable {
    default void printString() {
        System.out.println( "default implementation" );
    }
}

/**
 * Class which falls back to default implementation of {@link #printString()}
 */
public class WithDefault
    implements Printable
{
}

/**
 * Custom implementation of {@link #printString()}
 */
public class OverrideDefault
    implements Printable {
    @Override
    public void printString() {
        System.out.println( "overridden implementation" );
    }
}

Poniższe stwierdzenia

    new WithDefault().printString();
    new OverrideDefault().printString();

Wyprodukuje ten wynik:

default implementation
overridden implementation

Dostęp do innych metod interfejsu w ramach metody domyślnej

Możesz również uzyskać dostęp do innych metod interfejsu z poziomu metody domyślnej.

public interface Summable {
    int getA();

    int getB();

    default int calculateSum() {
        return getA() + getB();
    }
}

public class Sum implements Summable {
    @Override
    public int getA() {
        return 1;
    }

    @Override
    public int getB() {
        return 2;
    }
}

Następująca instrukcja wydrukuje 3 :

System.out.println(new Sum().calculateSum());

Można również użyć domyślnych metod wraz ze statycznymi metodami interfejsu:

public interface Summable {
    static int getA() {
        return 1;
    }

    static int getB() {
        return 2;
    }

    default int calculateSum() {
        return getA() + getB();
    }
}

public class Sum implements Summable {}

Następująca instrukcja wypisze również 3:

System.out.println(new Sum().calculateSum());

Dostęp do zastąpionych domyślnych metod z klasy implementującej

W klasach super.foo() będzie wyglądać tylko w superklasach. Jeśli chcesz wywołać domyślną implementację z superinterface, musisz zakwalifikować super z nazwą interfejsu: Fooable.super.foo() .

public interface Fooable {
    default int foo() {return 3;}
}

public class A extends Object implements Fooable {
    @Override
    public int foo() {
        //return super.foo() + 1; //error: no method foo() in java.lang.Object
        return Fooable.super.foo() + 1; //okay, returns 4
    }
}

Dlaczego warto korzystać z metod domyślnych?

Prosta odpowiedź jest taka, że pozwala ewoluować istniejący interfejs bez przerywania istniejących implementacji.

Na przykład masz interfejs Swim , który opublikowałeś 20 lat temu.

public interface Swim {
    void backStroke();
}

Wykonaliśmy świetną robotę, nasz interfejs jest bardzo popularny, istnieje wiele implementacji tego na całym świecie i nie masz kontroli nad ich kodem źródłowym.

public class FooSwimmer implements Swim {
    public void backStroke() {
         System.out.println("Do backstroke");
    }
}

Po 20 latach zdecydowałeś się dodać nową funkcjonalność do interfejsu, ale wygląda na to, że nasz interfejs jest zawieszony, ponieważ zepsuje istniejące implementacje.

Na szczęście Java 8 wprowadza zupełnie nową funkcję o nazwie Metoda domyślna.

Możemy teraz dodać nową metodę do interfejsu Swim .

public interface Swim {
    void backStroke();
    default void sideStroke() {
        System.out.println("Default sidestroke implementation. Can be overridden");
    }
}

Teraz wszystkie istniejące implementacje naszego interfejsu mogą nadal działać. Ale co najważniejsze, mogą wdrożyć nowo dodaną metodę we własnym czasie.

Jednym z największych powodów tej zmiany i jednym z jej największych zastosowań jest środowisko Java Collections. Oracle nie mógł dodać metody foreach do istniejącego interfejsu Iterable bez zerwania całego istniejącego kodu, który zaimplementował Iterable. Dodając domyślne metody, istniejąca implementacja Iterable odziedziczy domyślną implementację.

Pierwszeństwo klasy, klasy abstrakcyjnej i metody interfejsu

Implementacje w klasach, w tym deklaracje abstrakcyjne, mają pierwszeństwo przed wszystkimi domyślnymi ustawieniami interfejsu.

public interface Swim {
    default void backStroke() {
        System.out.println("Swim.backStroke");
    }
}

public abstract class AbstractSwimmer implements Swim {
    public void backStroke() {
        System.out.println("AbstractSwimmer.backStroke");
    }
}

public class FooSwimmer extends AbstractSwimmer {
}

Poniższe oświadczenie

new FooSwimmer().backStroke();

Będzie produkować

AbstractSwimmer.backStroke

public interface Swim {
    default void backStroke() {
        System.out.println("Swim.backStroke");
    }
}

public abstract class AbstractSwimmer implements Swim {
}

public class FooSwimmer extends AbstractSwimmer {
    public void backStroke() {
        System.out.println("FooSwimmer.backStroke");
    }
}

Poniższe oświadczenie

new FooSwimmer().backStroke();

Będzie produkować

FooSwimmer.backStroke

Domyślna metoda kolizji wielokrotnego dziedziczenia

Rozważ następny przykład:

public interface A {
    default void foo() { System.out.println("A.foo"); }
}

public interface B {
    default void foo() { System.out.println("B.foo"); }
}

Oto dwa interfejsy deklarujące default metodę foo z tym samym podpisem.

Jeśli spróbujesz extend oba interfejsy w nowym interfejsie, musisz wybrać dwa, ponieważ Java zmusza cię do jawnego rozwiązania tej kolizji.

Po pierwsze , możesz zadeklarować metodę foo z taką samą sygnaturą jak abstract , która zastąpi zachowanie A i B

public interface ABExtendsAbstract extends A, B {
    @Override
    void foo();
}

A kiedy implement ABExtendsAbstract w class , będziesz musiał zapewnić implementację foo :

public class ABExtendsAbstractImpl implements ABExtendsAbstract {
    @Override
    public void foo() { System.out.println("ABImpl.foo"); }
}

Po drugie , możesz podać zupełnie nową default implementację. Możesz także ponownie użyć kodu metod foo A i B uzyskując dostęp do zastąpionych metod domyślnych z klasy implementującej .

public interface ABExtends extends A, B {
    @Override
    default void foo() { System.out.println("ABExtends.foo"); }
}

A kiedy implement ABExtends w class , not będziesz musiał zapewniać implementacji foo :

public class ABExtendsImpl implements ABExtends {}


Modified text is an extract of the original Stack Overflow Documentation
Licencjonowany na podstawie CC BY-SA 3.0
Nie związany z Stack Overflow