305 votes

Implémentation de deux interfaces dans une classe avec la même méthode. Quelle méthode d'interface est surchargée ?

Deux interfaces avec les mêmes noms de méthodes et signatures. Mais implémentées par une seule classe, comment le compilateur va-t-il identifier quelle méthode correspond à 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;
  }
}

431voto

polygenelubricants Points 136838

Si un type implémente deux interfaces, et que chaque interface définir une méthode qui a une signature identique, alors en fait il n'y a qu'une seule méthode, et elles ne sont pas distinguables. Si, par exemple, les deux méthodes ont des types de retour contradictoires, alors il y aura une erreur de compilation. Il s'agit de la règle générale de l'héritage, de la substitution de méthode, de la dissimulation et des déclarations, et elle s'applique également aux conflits possibles non seulement entre deux méthodes héritées, mais aussi entre les deux méthodes. interface mais aussi une interface et un super class ou même simplement des conflits dus à l'effacement de type des génériques.


Exemple de compatibilité

Voici un exemple où vous avez un interface Gift qui a une present() (comme dans la présentation de cadeaux), et également une interface Guest qui a également un present() (c'est-à-dire que 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 se compile et s'exécute.

Notez que il n'y a qu'un seul @Override nécessaire ! !! . Cela s'explique par le fait que Gift.present() y 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'y a qu'une seule méthode à invoquer.


Exemple d'incompatibilité

Voici un exemple où les deux méthodes héritées ne sont PAS @Override -équivalent :

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

Cela réaffirme que le fait d'hériter de membres d'un interface doit obéir à la règle générale des déclarations de membres. Ici, nous avons Gift y Guest definir present() avec des types de retour incompatibles : un void l'autre boolean . Pour la même raison qu'on ne peut pas faire une void present() et un boolean present() dans un seul type, cet exemple donne lieu à une erreur de compilation.


Résumé

Vous pouvez hériter des méthodes qui sont @Override -sous réserve des exigences habituelles en matière de remplacement et de masquage des méthodes. Puisqu'ils SONT @Override -En fait, il n'y a qu'une seule méthode à mettre en œuvre, et il n'y a donc rien à distinguer/sélectionner.

Le compilateur n'a pas besoin d'identifier quelle méthode correspond à quelle interface, car une fois qu'il est déterminé qu'il s'agit de @Override -équivalent, c'est la même méthode.

Résoudre les incompatibilités potentielles peut être une tâche délicate, mais c'est une autre question.

Références

0 votes

Merci - cela a été utile. Cependant, j'avais une autre question sur l'incompatibilité, que j'ai postée en tant qu'"article". nouvelle question

4 votes

BTW Cela change un peu avec le support de default dans Java 8.

0 votes

Les classes composites pour résoudre les incompatibilités potentielles peuvent être la solution :), mais je n'ai jamais eu ce problème, et pourtant il est évident que cela peut arriver.

42voto

Peter Lawrey Points 229686

Cette question a été marquée comme un double de la question précédente. https://stackoverflow.com/questions/24401064/understanding-and-solving-the-diamond-problems-in-java

Il faut Java 8 pour obtenir un problème d'héritage multiple, mais ce n'est toujours pas un problème de diamant 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 JB Nizet l'a fait remarquer, vous pouvez résoudre ce problème en écrasant les données.

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.

0 votes

Wow. c'est nouveau pour moi. Pourquoi ont-ils dû créer un défaut dans Java 8 ?

4 votes

Pour faciliter l'ajout de nouvelles méthodes aux interfaces (spécifiquement les interfaces de collections) sans casser 60% du codebase.

1 votes

@BoratSagdiyev La raison principale était de soutenir les fermetures et de les rendre plus utiles. Voir Collection.stream(). Jetez un œil à List.sort() docs.oracle.com/javase/8/docs/api/java/util/ Ils ont ajouté une méthode pour toutes les Listes, sans avoir à modifier une implémentation spécifique. Ils ont ajouté Collection.removeIf() qui est utile

23voto

Ash Points 5561

En ce qui concerne le compilateur, ces deux méthodes sont identiques. Il y aura une seule 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. Si elles sont contractuellement différentes (conformément à la documentation de chaque interface), vous aurez des problèmes.

2 votes

Il explique pourquoi Java ne vous permet pas étend plus d'une classe

1 votes

@ArthurRonald, en fait, ça semble juste lié. Cependant, IMO, la classe qui étend plus d'une classe peut rencontrer le problème du diamant (qui est la duplication de l'état de l'objet dans la classe la plus dérivée) et c'est très probablement la raison pour laquelle Java a éloigné ses utilisateurs des problèmes. D'autre part, une classe qui met en œuvre plus d'une classe peut jamais se heurtent au problème du diamant simplement parce que les interfaces ne fournissent pas d'état aux objets. Et le problème est purement dû aux limitations de la syntaxe - incapacité de qualifier complètement l'appel de fonction.

18voto

Michael Borgwardt Points 181658

Il n'y a rien à identifier. Les interfaces ne prescrivent qu'un nom de méthode et une signature. Si les deux interfaces ont une méthode ayant exactement le même nom et la même signature, la classe d'implémentation peut implémenter les deux méthodes de l'interface avec une seule méthode concrète.

Toutefois, si le sémantique les contrats des deux interfaces sont contradictoires, vous avez pratiquement perdu ; vous ne pouvez donc pas implémenter les deux interfaces dans une seule classe.

5voto

Paul Whelan Points 8620

S'ils sont tous les deux identiques, ça n'a pas d'importance. Il les implémente toutes les deux avec une seule méthode concrète par méthode d'interface.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X