Czy wartości domyślne w JDK 8 są formą wielokrotnego dziedziczenia w Javie?

83

Nowa funkcja dostępna w JDK 8 umożliwia dodawanie do istniejącego interfejsu przy zachowaniu zgodności binarnej.

Składnia jest podobna do

public interface SomeInterface() {
  void existingInterface();
  void newInterface() default SomeClass.defaultImplementation;
}

W ten sposób w przypadku wszystkich istniejących implementacji, SomeInterfacegdy aktualizują się do nowej wersji, nie wszystkie nagle mają błędy kompilacji newInterface().

Chociaż jest to fajne, co się dzieje, gdy wdrażasz dwa interfejsy, które dodały nową domyślną metodę, której nie zaimplementowałeś? Pozwólcie, że wyjaśnię na przykładzie.

public interface Attendance {
   boolean present() default DefaultAttendance.present;
}

public interface Timeline {
   boolean present() default DefaultTimeline.present;
}

public class TimeTravelingStudent implements Attendance, Timeline {

}

// which code gets called?
new TimeTravelingStudent().present();

Czy zostało to już zdefiniowane jako część JDK 8?

Znalazłem bogów Jawy, którzy mówią tutaj o czymś podobnym http://cs.oswego.edu/pipermail/lambda-lib/2011-Feb February/000068.html , ale jest to część prywatnej listy mailingowej i nie mogę ich bezpośrednio zapytać.

Zobacz to, aby uzyskać więcej informacji o tym, jak wartości domyślne będą używane w JDK 8 i rozszerzaniu interfejsu Collection o obsługę lambd: https://oracleus.wingateweb.com/published/oracleus2011/sessions/25066/25066_Cho223662.pdf

Pirolistyczne
źródło
Sesja wideo do obejrzenia jest tutaj. Medianetwork.oracle.com/video/player/1113272518001 To jest projektant, który mówi o funkcji zwanej Wirtualne rozszerzenia. Mówi również o tym, jak to nie łamie wstecznej kompatybilności.
Peter Lawrey
Tak więc odpowiedź na mój przykład brzmi: obecnie pojawia się błąd kompilatora. Ale są otwarci na inne rozwiązania.
Pyrolistic
Innym przydatnym materiałem do tego jest cr.openjdk.java.net/~briangoetz/lambda/…
MohamedSanaulla
Uwaga dodatkowa: ostateczna składnia domyślnych implementacji metod interfejsu okazała się inna (blok kodu zamiast odwołania do klasy zewnętrznej).
Joachim Sauer
4
Java zawsze miała wielokrotne dziedziczenie typów . Metody domyślne dodają wielokrotne dziedziczenie zachowania , ale nie stanu . (Wielokrotne dziedziczenie stanu w językach takich jak C ++ jest przyczyną większości problemów).
Brian Goetz,

Odpowiedzi:

66

Odpowiedź na zduplikowaną operację brzmi:

Aby rozwiązać problem wielokrotnego dziedziczenia, klasa implementująca dwa interfejsy zapewniające domyślną implementację dla tej samej nazwy metody i podpisu musi zapewniać implementację metody. [Pełny artykuł]

Moja odpowiedź na Twoje pytanie brzmi: Tak, jest to forma dziedziczenia wielokrotnego, ponieważ możesz dziedziczyć zachowanie po różnych rodzicach. Brakuje dziedziczenia stanów, czyli atrybutów.

H-Man2
źródło
2
+1: W przypadku konfliktu zajmie to bardziej szczegółową implementację niż mniej szczegółową, umożliwiając określenie, która powinna być, jeśli wystąpi konflikt.
Peter Lawrey
@ H-Man2 Nie rozumiem. Jeśli to prawda, oznacza to, że złamałoby to zgodność binarną. Może to nie jest tak złe, jak zerwanie zgodności binarnej, aby zapobiec wielokrotnemu dziedziczeniu, zerwanie zgodności binarnej jest do bani.
Pyrolistic
@PeterLawrey, który z nich jest bardziej szczegółowy w moim przykładzie?
Pyrolistic
@Pyrolistical: Nie, nie narusza to kompatybilności, ponieważ kompilator może przetłumaczyć domyślną implementację na normalne wywołanie metody. Myślę, że jest to bardziej makro niż prawdziwe dziedziczenie, ale może symulować aspekty wielokrotnego dziedziczenia. Być może film Petera zawiera bardziej szczegółową odpowiedź.
H-Man2
faktycznie możemy obejść brakujące stany, mając abstrakcyjne
metody pobierające
8

Wiem, że to stary post, ale ponieważ pracuję nad tym ...

Będziesz miał błąd od kompilatora, informujący, że:

Klasa  TimeTravelingStudent dziedziczy niepowiązane wartości domyślne dla present () z typów Attendance i Timeline odwołanie do present jest niejednoznaczne, zarówno metoda present () w Timeline, jak i metoda present () w Attendance match.

Enkk
źródło
7

Jest ich dwóch scenariusze:

1) Po pierwsze, o czym wspomniano, gdzie nie ma konkretnego interfejsu

public interface A {
   default void doStuff(){ /* implementation */ }
}

public interface B {
   default void doStuff() { /* implementation */ } 
}

public class C implements A, B {
// option 1: own implementation
// OR
// option 2: use new syntax to call specific interface or face compilation error
  void doStuff(){
      B.super.doStuff();
  }
}

2) Po drugie, gdy JEST bardziej szczegółowy interfejs:

   public interface A {
       default void doStuff() { /* implementation */ } 
    }

    public interface B extends A {
       default void doStuff() { /* implementation */ } 
    }

    public class C implements A, B {
    // will use method from B, as it is "closer" to C
    }
Andrejs
źródło
4

Moja odpowiedź na Twoje pytanie brzmi: Tak, jest to forma dziedziczenia wielokrotnego, ponieważ możesz dziedziczyć zachowanie po różnych rodzicach. Brakuje dziedziczenia stanów, czyli atrybutów.

Tak, ale możesz dodać metody pobierające i ustawiające do interfejsu, które klasy implementujące muszą następnie zaimplementować. Niemniej jednak klasy implementujące nie dziedziczą atrybutów. Tak więc, AFAICS, jest to bardziej rozwiązanie w stylu cechy niż rozwiązanie w stylu wielokrotnego dziedziczenia.

OlliP
źródło
4

Krótko mówiąc: jest to błąd czasu kompilacji, musi ręcznie przesłonić metodę w implementacji.


Cel metody domyślnej

Głównym celem wprowadzenia domyślnej metody w Javie 8 jest umożliwienie rozszerzania interfejsu bez przerywania istniejących implementacji (istnieje tak wiele bibliotek Java innych firm).

I multiple inheritancepodobnie jak w C ++, tak naprawdę należy go unikać, zdecydowanie nie jest to celem domyślnej metody w Javie.


Jak zastąpić

2 opcje:

  • Zastąp metodę, używając jej własnej logiki.
  • Zastąp metodę, wywołaj jedną z metod interfejsu za pomocą super, format:<interface_name>.super.<method_name>();

Porady:

  • metoda z interfejsu jest domyślnie publiczna, więc nie zapomnij dodać publicsłowa kluczowego podczas jej zastępowania.
user218867
źródło
2

Jeśli ktoś nadal szuka odpowiedzi, w przypadku, gdy klasa implementuje dwa interfejsy z tą samą metodą domyślną, klasa musi rozwiązać tę niejednoznaczność, dostarczając własną implementację. Więcej informacji na temat dziedziczenia w metodach domyślnych można znaleźć w tym samouczku.

mithil-studytrails
źródło
0

Pytanie „Jak rozróżnimy metody” zostało postawione na Stackoverflow i odniosło się do tego pytania konkretnych metod w interfejsach Java1.8

Oto przykład, który powinien odpowiedzieć na to pytanie:

interface A{
default public void m(){
System.out.println("Interface A: m()");
}
}

interface B{
default public void m(){
System.out.println("Interface B: m()");
}
}

 class C implements A,B { 

 public void m(){
  System.out.println("Concrete C: m()");   
 }

public static void main(String[] args) {
   C aC = new C();
   aC.m();
   new A(){}.m();
   new B(){}.m();
}
}

Klasa C powyżej musi implementować swoją własną konkretną metodę interfejsów A i B. Mianowicie:

 public void m(){
  System.out.println("Interface C: m()");   
 }

Aby połączyć się z realizacji betonowej o sposobie ze interfejsu konkretnego , można utworzyć wystąpienia do interfejsu i jawnie wywołać w sposób konkretny tego interfejsu

Na przykład poniższy kod wywołuje konkretną implementację metody m () z interfejsu A :

new A(){}.m();

Wynik powyższego byłby:

Interfejs A: m ()

Mark Burleigh
źródło
-1

O ile mi wiadomo, nie jest to dziedziczenie wielokrotne, ponieważ są bezpaństwowcami. Dlatego wirtualne metody rozszerzające nie obsługują pełnej funkcjonalności obiektu lub klasy.

Piotr
źródło