web-dev-qa-db-fra.com

Implémentation de deux interfaces avec deux méthodes par défaut de la même signature sous Java 8

Supposons que j'ai deux interfaces:

public interface I1
{
    default String getGreeting() {
        return "Good Morning!";
    }
}

public interface I2
{
    default String getGreeting() {
        return "Good Afternoon!";
    }
}

Si je veux implémenter les deux, quelle implémentation sera utilisée?

public class C1 implements I1, I2
{
    public static void main(String[] args)
    {
        System.out.println(new C1().getGreeting());
    }
}
34
kavai77

C'est une erreur de compilation. Vous ne pouvez pas avoir deux implémentations à partir de deux interfaces.

Cependant, il est correct si vous implémentez la méthode getGreeting dans C1:

public class C1 implements I1, I2 // this will compile, bacause we have overridden getGreeting()
{
    public static void main(String[] args)
    {
        System.out.println(new C1().getGreeting());
    }

    @Override public String getGreeting()
    {
        return "Good Evening!";
    }
}

Je veux juste ajouter que même si la méthode dans I1 est abstraite et que par défaut dans I2, vous ne pouvez pas les implémenter. Donc, ceci est aussi une erreur de compilation:

public interface I1
{
    String getGreeting();
}

public interface I2
{
    default String getGreeting() {
        return "Good afternoon!";
    }
}

public class C1 implements I1, I2 // won't compile
{
    public static void main(String[] args)
    {
        System.out.println(new C1().getGreeting());
    }
}
51
kavai77

Ce n'est pas spécifique à la question. Mais je pense toujours que cela ajoute une certaine valeur au contexte. En complément de la réponse de @ toni77, j'aimerais ajouter que la méthode par défaut peut être appelée à partir d'une classe d'implémentation, comme indiqué ci-dessous. Dans le code ci-dessous, la méthode par défaut getGreeting() from interface I1 est appelée à partir d'une méthode remplacée:

public interface I1 {
     default String getGreeting() {
        return "Good Morning!";
     }
}

public class C1 implements I1, I2 {       
    @override
    public String getGreeting() {
        return I1.super.getGreeting();
    }
}
21
Keerthivasan

Si une classe implémente 2 interfaces ayant toutes deux une méthode par défaut Java-8 avec la même signature (comme dans votre exemple), la classe implémentée est tenue de remplacer la méthode . La classe peut toujours accéder à la méthode par défaut en utilisant I1.super.getGreeting();. Il peut accéder à l'un ou l'autre, ou aux deux. Donc, ce qui suit serait une implémentation valide de C1

public class C1 implements I1, I2{
    public static void main(String[] args)
    {
        System.out.println(new C1().getGreeting());
    }

    @Override //class is obliged to override this method
    public String getGreeting() {
        //can use both default methods
        return I1.super.getGreeting()+I2.super.getGreeting();
    }

    public String useOne() {
        //can use the default method within annother method
        return "One "+I1.super.getGreeting();
    }

    public String useTheOther() {
        //can use the default method within annother method
        return "Two "+I2.super.getGreeting();
    }


}
7
Richard Tingle

Il existe un cas où cela fonctionne réellement selon les règles de résolution. Si l'une des interfaces étend l'une des autres.

En utilisant l'exemple ci-dessus:

public interface I2 extends I1 {
    default String getGreeting() {
        return "Good Afternoon!";
    }
}

Le résultat serait:

Bonne après-midi!

Cependant, je crois que cela va être un gros problème. La raison principale pour les interfaces par défaut est de permettre aux développeurs de bibliothèques de faire évoluer leurs API sans casser les implémenteurs.

Naturellement, ils ne permettent pas aux méthodes de compiler sans la structure d'héritage via une extension, car un développeur de bibliothèque pourrait potentiellement avoir un comportement de piratage.

Cependant, cela peut être auto-destructeur. Si une classe implémente deux interfaces qui ne sont pas liées depuis une vue hiérarchique, mais qui définissent toutes les deux la même signature de méthode par défaut, la classe qui étend les deux interfaces ne sera pas compilée. (comme démontré ci-dessus)

Il est concevable que deux développeurs de bibliothèques différents puissent décider d’ajouter des méthodes par défaut à des moments différents en utilisant des signatures communes; en fait, il est probable que cela se produise dans les bibliothèques qui implémentent des concepts similaires tels que les bibliothèques de mathématiques. S'il vous arrive d'être l'âme désolée implémentant les deux interfaces dans la même classe, vous serez cassé lors de la mise à jour. 

2
TechTrip

Je crois que la règle est que la classe implémentant les méthodes par défaut en double 'doit' écraser l'implémentation. Les éléments suivants sont compilés et fonctionnent correctement ...

public class DupeDefaultInterfaceMethods {

interface FirstAbility {
    public default boolean doSomething() {
        return true;
    }
}

interface SecondAbility {
    public default boolean doSomething() {
        return true;
    }
}

class Dupe implements FirstAbility, SecondAbility {
    @Override
    public boolean doSomething() {
        return false;
    }
}

public static void main(String[] args) {
    DupeDefaultInterfaceMethods ddif = new DupeDefaultInterfaceMethods();
    Dupe dupe = ddif.new Dupe();
    System.out.println(dupe.doSomething());

    }
}

> false
0
Eddie B