Deux interfaces avec les mêmes noms de méthodes et signatures. Mais implémenté par une seule classe, alors comment le compilateur identifiera quelle méthode est pour quelle interface?
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;
}
}
Si un type implémente deux interfaces et que chaque interface
définit une méthode ayant une signature identique, il n'existe en réalité qu'une seule méthode qui ne peut pas être distinguée. Si, par exemple, les deux méthodes ont des types de retour en conflit, ce sera une erreur de compilation. Il s’agit de la règle générale d’héritage, de substitution de méthode, de masquage et de déclaration. Elle s’applique également aux conflits possibles non seulement entre 2 méthodes interface
héritées, mais également une interface
et un super class
méthode, ou même simplement des conflits dus à l’effacement des types de génériques.
Voici un exemple où vous avez un interface Gift
, qui a une méthode present()
(comme dans la présentation de cadeaux), ainsi qu'un interface Guest
, qui a également une méthode present()
( comme dans l'invité est présent et non absent).
Presentable johnny
est à la fois un Gift
et 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!!!"
}
}
L'extrait ci-dessus est compilé et exécuté.
Notez que il n'y en a qu'un @Override
nécessaire !!! . En effet, Gift.present()
et Guest.present()
sont "@Override
- équivalent" ( JLS 8.4.2 ).
Ainsi, johnny
n'a qu'une seule implémentation de present()
, et peu importe la façon dont vous traitez johnny
, que ce soit en tant que Gift
ou en tant que Guest
, il n’ya qu’une méthode à invoquer.
Voici un exemple où les deux méthodes héritées ne sont pas équivalentes à @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"
}
Ceci réitère en outre que l'héritage des membres d'un interface
doit obéir à la règle générale des déclarations de membres. Ici, nous avons Gift
et Guest
définir present()
avec des types de retour incompatibles: l'un void
l'autre boolean
. Pour la même raison que vous ne pouvez pas associer void present()
et boolean present()
à un type, cet exemple génère une erreur de compilation.
Vous pouvez hériter de méthodes équivalentes à @Override
-, sous réserve des exigences habituelles en matière de substitution et de masquage de méthode. Comme ils SONT@Override
- équivalents, il n’existe en réalité qu’une méthode à mettre en œuvre et il n’ya donc rien à distinguer/sélectionner.
Le compilateur n'a pas à identifier quelle méthode correspond à quelle interface, car une fois qu'ils sont déterminés comme étant @Override
- équivalent, ils utilisent la même méthode.
La résolution des incompatibilités potentielles peut être une tâche délicate, mais c’est un tout autre problème.
Cela a été marqué comme un doublon à cette question https://stackoverflow.com/questions/24401064/understanding-and-solving-the-diamond-problems-in-Java
Vous avez besoin de Java 8 pour résoudre un problème d'héritage multiple, mais il ne s'agit toujours pas d'un problème de diamon en tant que tel.
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.
Comme le commente JB Nizet, vous pouvez corriger cela.
class AB implements A, B {
public void hi() { A.super.hi(); }
}
Cependant, vous n'avez pas de problème avec
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.
En ce qui concerne le compilateur, ces deux méthodes sont identiques. Il y aura une implémentation des deux.
Ce n'est pas un problème si les deux méthodes sont effectivement identiques, en ce sens qu'elles devraient avoir la même implémentation. S'ils sont contractuellement différents (conformément à la documentation de chaque interface), vous aurez des problèmes.
Il n'y a rien à identifier. Les interfaces interdisent uniquement un nom de méthode et une signature. Si les deux interfaces ont une méthode portant exactement le même nom et la même signature, la classe d'implémentation peut implémenter les deux méthodes d'interface avec une seule méthode concrète.
Cependant, si les contrats sémantique de la méthode à deux interfaces sont contradictoires, vous avez quasiment perdu la partie; vous ne pouvez pas alors implémenter les deux interfaces dans une même classe.
Comme dans l'interface, nous ne faisons que déclarer des méthodes. La classe concrète qui implémente ces deux interfaces comprend qu'il n'y a qu'une seule méthode (comme vous l'avez décrit, les deux ont le même nom dans le type de retour). il ne devrait donc pas y avoir de problème. Vous pourrez définir cette méthode dans une classe concrète.
Mais lorsque deux interfaces ont une méthode portant le même nom mais un type de retour différent et que vous implémentez deux méthodes dans une classe concrète:
Veuillez regarder le code ci-dessous:
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;
}
}
lorsque le compilateur obtient la méthode "public void print ()", il cherche d'abord l'interface et l'obtient. Mais il donne tout de même une erreur de compilation indiquant que le type de retour n'est pas compatible avec la méthode d'InterfaceB.
Donc, ça se passe mal pour le compilateur.
De cette manière, vous ne pourrez pas implémenter deux interfaces ayant une méthode du même nom mais un type de retour différent.
Essayez d'implémenter l'interface de manière anonyme.
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(){
}
}
Eh bien, si les deux sont identiques, cela n'a pas d'importance. Il implémente les deux avec une seule méthode concrète par méthode d'interface.