Implementazione di due interfacce in una class con lo stesso metodo. Quale metodo di interfaccia è sovrascritto?

Due interfacce con gli stessi nomi e firme dei metodi. Ma implementato da una singola class, quindi come il compilatore identificherà il metodo per quale interfaccia?

Ex:

interface A{ int f(); } interface B{ int f(); } class Test implements A, B{ public static void main(String... args) throws Exception{ } @Override public int f() { // from which interface A or B return 0; } } 

Se un tipo implementa due interfacce e ciascuna interface definisce un metodo con firma identica, in effetti esiste un solo metodo e non sono distinguibili. Se, ad esempio, i due metodi hanno tipi di ritorno in conflitto, allora sarà un errore di compilazione. Questa è la regola generale dell’ereditarietà, metodo di sovrascrittura, occultamento e dichiarazioni e si applica anche a possibili conflitti non solo tra 2 metodi di interface ereditati, ma anche interface e un metodo di super class , o anche solo conflitti dovuti alla cancellazione di tipo dei generici .


Esempio di compatibilità

Ecco un esempio in cui hai interface Gift , che ha un metodo present() (come in, presenta regali), e anche interface Guest , che ha anche un metodo present() (come in, l’ospite è presente e non è presente ).

Presentable johnny è sia un Gift che un 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!!!" } } 

Lo snippet sopra riportato viene compilato e eseguito.

Nota che c’è solo un @Override necessario !!! . Questo perché Gift.present() e Guest.present() sono ” @Override ” ( JLS 8.4.2 ).

Quindi, johnny ha solo un’implementazione di present() , e non importa come tratti johnny , sia come Gift o come Guest , c’è solo un metodo da invocare.


Esempio di incompatibilità

Ecco un esempio in cui i due metodi ereditati NON sono @Override :

 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" } 

Ciò ribadisce ulteriormente che l’ereditare i membri da interface deve obbedire alla regola generale delle dichiarazioni dei membri. Qui abbiamo Gift e Guest definire present() con tipi di ritorno incompatibili: uno void l’altro boolean . Per lo stesso motivo per cui non è ansible presentare un void present() e un boolean present() in un tipo, questo esempio genera un errore di compilazione.


Sommario

È ansible ereditare metodi che sono @Override , in base ai normali requisiti del metodo di esclusione e di occultamento. Dal momento che ARE @Override , in effetti, c’è solo un metodo da implementare, e quindi non c’è nulla da distinguere / selezionare.

Il compilatore non deve identificare quale metodo è per quale interfaccia, perché una volta che sono determinati a essere @Override , sono lo stesso metodo.

Risolvere potenziali incompatibilità può essere un compito difficile, ma questo è un altro problema.

Riferimenti

  • Firma del metodo JLS 8.4.2
  • JLS 8.4.8 Ereditarietà, sovrascrittura e occultamento
  • Requisiti JLS 8.4.8.3 in Override e Hiding
  • 8.4.8.4 Metodi ereditari con firme equivalenti alla sovrascrittura
    • “È ansible che una class erediti più metodi con firme equivalenti a override.”

Per quanto riguarda il compilatore, questi due metodi sono identici. Ci sarà una implementazione di entrambi.

Questo non è un problema se i due metodi sono effettivamente identici, in quanto dovrebbero avere la stessa implementazione. Se sono contrattualmente diversi (come da documentazione per ogni interfaccia), sarai nei guai.

Questo è stato contrassegnato come un duplicato di questa domanda https://stackoverflow.com/questions/24401064/understanding-and-solving-the-diamond-problems-in-java

Hai bisogno di Java 8 per ottenere un problema di ereditarietà multipla, ma non è ancora un problema di diamon in quanto tale.

 interface A { default void hi() { System.out.println("A"); } } interface B { default void hi() { System.out.println("B"); } } class AB implements A, B { // won't compile } new AB().hi(); // won't compile. 

Come commenta JB Nizet, puoi sistemare questo mio imperativo.

 class AB implements A, B { public void hi() { A.super.hi(); } } 

Tuttavia, non hai problemi con

 interface D extends A { } interface E extends A { } interface F extends A { default void hi() { System.out.println("F"); } } class DE implement D, E { } new DE().hi(); // prints A class DEF implement D, E, F { } new DEF().hi(); // prints F as it is closer in the heirarchy than A. 

Non c’è nulla da identificare. Le interfacce vietano solo un nome e una firma del metodo. Se entrambe le interfacce hanno un metodo con esattamente lo stesso nome e firma, la class di implementazione può implementare entrambi i metodi di interfaccia con un singolo metodo concreto.

Tuttavia, se i contratti semantici dei due metodi di interfaccia sono in contraddizione, hai praticamente perso; allora non puoi implementare entrambe le interfacce in una singola class.

Prova a implementare l’interfaccia come anonima.

 public class MyClass extends MySuperClass implements MyInterface{ MyInterface myInterface = new MyInterface(){ /* Overrided method from interface */ @override public void method1(){ } }; /* Overrided method from superclass*/ @override public void method1(){ } } 

Come nell’interfaccia, stiamo solo dichiarando i metodi, la class concreta che implementa entrambe le interfacce comprende che esiste un solo metodo (come hai descritto entrambi hanno lo stesso nome nel tipo restituito). quindi non dovrebbe esserci un problema con esso. Sarai in grado di definire quel metodo nella class concreta.

Ma quando due interfacce hanno un metodo con lo stesso nome ma un tipo di ritorno diverso e si implementano due metodi nella class concreta:

Si prega di guardare sotto il codice:

 public interface InterfaceA { public void print(); } public interface InterfaceB { public int print(); } public class ClassAB implements InterfaceA, InterfaceB { public void print() { System.out.println("Inside InterfaceA"); } public int print() { System.out.println("Inside InterfaceB"); return 5; } } 

quando il compilatore ottiene il metodo “public void print ()” prima appare in InterfaceA e lo ottiene.Ma ancora dà errore in fase di compilazione che il tipo restituito non è compatibile con il metodo di InterfaceB.

Quindi va in tilt per il compilatore.

In questo modo, non sarà ansible implementare due interfacce con un metodo con lo stesso nome ma un diverso tipo di restituzione.

Beh, se sono entrambi uguali, non importa. Implementa entrambi con un singolo metodo concreto per metodo di interfaccia.