Jeśli typ implementuje dwa interfejsy, a każdy z nich interface
definiuje metodę o identycznej sygnaturze, to w efekcie istnieje tylko jedna metoda i nie można ich rozróżnić. Jeśli, powiedzmy, dwie metody mają sprzeczne typy zwrotów, oznacza to błąd kompilacji. Jest to ogólna zasada dziedziczenia, zastępowania metod, ukrywania i deklaracji, i ma zastosowanie również do ewentualnych konfliktów nie tylko między 2 odziedziczonymi interface
metodami, ale także metodą interface
super i super class
, a nawet po prostu konfliktów spowodowanych usunięciem typów ogólnych.
Przykład kompatybilności
Oto przykład, w którym masz metodę interface Gift
, która ma present()
metodę (jak w, prezentowanie prezentów), a także interface Guest
, która również ma present()
metodę (jak w, gość jest obecny i nieobecny).
Presentable johnny
jest zarówno Gift
a Guest
.
public class InterfaceTest {
interface Gift { void present(); }
interface Guest { void present(); }
interface Presentable extends Gift, Guest { }
public static void main(String[] args) {
Presentable johnny = new Presentable() {
@Override public void present() {
System.out.println("Heeeereee's Johnny!!!");
}
};
johnny.present(); // "Heeeereee's Johnny!!!"
((Gift) johnny).present(); // "Heeeereee's Johnny!!!"
((Guest) johnny).present(); // "Heeeereee's Johnny!!!"
Gift johnnyAsGift = (Gift) johnny;
johnnyAsGift.present(); // "Heeeereee's Johnny!!!"
Guest johnnyAsGuest = (Guest) johnny;
johnnyAsGuest.present(); // "Heeeereee's Johnny!!!"
}
}
Powyższy fragment kompiluje się i uruchamia.
Pamiętaj, że jest tylko jedna @Override
konieczna !!! . To dlatego, że Gift.present()
i Guest.present()
są " @Override
-equivalent" ( JLS 8.4.2 ).
Tak więc, johnny
ma tylko jedną implementację o present()
, i to nie ma znaczenia, w jaki sposób leczyć johnny
, czy jako Gift
lub jako Guest
, że jest tylko jeden sposób, aby wywołać.
Przykład niezgodności
Oto przykład, w którym dwie odziedziczone metody NIE są @Override
równoważne:
public class InterfaceTest {
interface Gift { void present(); }
interface Guest { boolean present(); }
interface Presentable extends Gift, Guest { } // DOES NOT COMPILE!!!
// "types InterfaceTest.Guest and InterfaceTest.Gift are incompatible;
// both define present(), but with unrelated return types"
}
To jeszcze bardziej przypomina, że dziedziczenie członków po konieczności interface
musi być zgodne z ogólną zasadą deklaracji członków. Tutaj mamy Gift
i Guest
definiujemy present()
niekompatybilne typy zwrotów: jeden void
drugi boolean
. Z tego samego powodu, że nie można void present()
oraz boolean present()
w jednym rodzaju, przykład ten powoduje błąd kompilacji.
Podsumowanie
Możesz dziedziczyć metody, które są @Override
ekwiwalentne, z zastrzeżeniem zwykłych wymagań zastępowania i ukrywania metod. Ponieważ SĄ @Override
-equivalent, skutecznie istnieje tylko jeden sposób do wdrożenia, a więc nie ma nic do odróżnienia / wyboru.
Kompilator nie musi określać, która metoda jest przeznaczona dla @Override
danego interfejsu, ponieważ po ustaleniu , że są równoważne, są one tą samą metodą.
Rozwiązanie potencjalnych niezgodności może być trudnym zadaniem, ale to zupełnie inna kwestia.
Bibliografia
default
metod w Javie 8.Foo
iBar
. Zasadniczo masz klasę, która implementuje jeden z interfejsów, powiedzmyFoo
, i udostępniaBar asBar()
metodę zwracającą klasę wewnętrzną, która implementuje drugiBar
interfejs. Nie jest idealny, ponieważ twoja klasa ostatecznie nie jest „barem”, ale może być przydatna w niektórych okolicznościach.Oznaczono to jako duplikat tego pytania /programming/24401064/understanding-and-solving-the-diamond-problems-in-java
Potrzebujesz Java 8, aby uzyskać problem wielokrotnego dziedziczenia, ale nadal nie jest to problem diamon jako taki.
Jak komentuje JB Nizet, możesz to naprawić.
Jednak nie masz z tym problemu
źródło
hi()
(aby naprawić niejednoznaczność). Na przykład, wdrażając go,A.super.hi()
aby wybrać wdrożenie w taki sam sposób, jak A.Jeśli chodzi o kompilator, te dwie metody są identyczne. Będzie jedno wdrożenie obu.
Nie stanowi to problemu, jeśli dwie metody są faktycznie identyczne, ponieważ powinny mieć tę samą implementację. Jeśli różnią się w umowie (zgodnie z dokumentacją dla każdego interfejsu), będziesz miał kłopoty.
źródło
Nie ma nic do zidentyfikowania. Interfejsy zakazują jedynie nazwy metody i podpisu. Jeśli oba interfejsy mają metodę o dokładnie takiej samej nazwie i podpisie, klasa implementująca może zaimplementować obie metody interfejsu za pomocą jednej konkretnej metody.
Jeśli jednak kontrakty semantyczne metody dwóch interfejsów są ze sobą sprzeczne, prawie się zgubiłeś; nie można wtedy wdrożyć obu interfejsów w jednej klasie.
źródło
Spróbuj zaimplementować interfejs jako anonimowy.
źródło
Podobnie jak w interfejsie, po prostu deklarujemy metody, konkretna klasa, która implementuje oba interfejsy rozumie, że istnieje tylko jedna metoda (jak opisano, obie mają taką samą nazwę w typie zwracanym). więc nie powinno być z tym problemu. Będziesz mógł zdefiniować tę metodę w konkretnej klasie.
Ale gdy dwa interfejsy mają metodę o tej samej nazwie, ale innym typie zwrotu, a implementujesz dwie metody w konkretnej klasie:
Proszę spojrzeć na poniższy kod:
kiedy kompilator otrzyma metodę „public void print ()”, najpierw szuka w interfejsie A i dostaje go, ale nadal daje błąd czasu kompilacji, że typ zwracany jest niezgodny z metodą interfejsuB.
Więc idzie na kompilator.
W ten sposób nie będzie można wdrożyć dwóch interfejsów o metodzie o tej samej nazwie, ale o innym typie zwrotu.
źródło
Cóż, jeśli oba są takie same, to nie ma znaczenia. Obie implementują je za pomocą jednej konkretnej metody dla każdej metody interfejsu.
źródło