web-dev-qa-db-fra.com

Java - Collision de nom de méthode dans l'implémentation d'interface

Si j'ai deux interfaces, toutes deux très différentes, mais avec la même signature de méthode, comment faire en sorte qu'une classe implémente les deux sans être obligée d'écrire une seule méthode qui serve à la fois les interfaces et l'écriture d'une logique compliquée dans la méthode une implémentation qui vérifie le type d’objet de l’appel et appelle le code approprié?

En C #, ceci est surmonté par ce qu'on appelle l'implémentation d'interface explicite. Existe-t-il un moyen équivalent en Java?

85
Bhaskar

Non, il n'y a aucun moyen d'implémenter la même méthode de deux manières différentes dans une même classe en Java.

Cela peut conduire à de nombreuses situations confuses, raison pour laquelle Java ne l’a pas autorisé.

interface ISomething {
    void doSomething();
}

interface ISomething2 {
    void doSomething();
}

class Impl implements ISomething, ISomething2 {
   void doSomething() {} // There can only be one implementation of this method.
}

Ce que vous pouvez faire est de composer une classe sur deux classes qui implémentent chacune une interface différente. Ensuite, cette classe aura le comportement des deux interfaces.

class CompositeClass {
    ISomething class1;
    ISomething2 class2;
    void doSomething1(){class1.doSomething();}
    void doSomething2(){class2.doSomething();}
}
72
jjnguy

Il n'y a pas vraiment de moyen de résoudre cela en Java. Vous pouvez utiliser les classes internes comme solution de contournement:

interface Alfa { void m(); }
interface Beta { void m(); }
class AlfaBeta implements Alfa {
    private int value;
    public void m() { ++value; } // Alfa.m()
    public Beta asBeta() {
        return new Beta(){
            public void m() { --value; } // Beta.m()
        };
    }
}

Bien qu’il ne permette pas les conversions de AlfaBeta à Beta, les transferts en différé sont généralement diaboliques, et si on peut s’attendre à ce qu'une instance Alfa ait souvent aussi un aspect Beta, et voulez pouvoir le convertir en Beta, vous pouvez créer une sous-interface de Alfa avec Beta asBeta() dedans.

13
gustafc

Si vous rencontrez ce problème, c'est probablement parce que vous utilisez héritage où vous devriez utiliser délégation. Si vous devez fournir deux interfaces différentes, bien que similaires, pour le même modèle de données sous-jacent, vous devez utiliser un vue fournir un accès économique aux données en utilisant une autre interface.

Pour donner un exemple concret dans le dernier cas, supposons que vous souhaitiez implémenter Collection et MyCollection (qui n'hérite pas de Collection et possède une interface incompatible). Vous pouvez fournir les fonctions Collection getCollectionView() et MyCollection getMyCollectionView() qui fournissent une implémentation légère de Collection et MyCollection, en utilisant les mêmes données sous-jacentes. 

Dans le premier cas, supposons que vous souhaitiez réellement un tableau d'entiers et un tableau de chaînes. Au lieu d'hériter à la fois de List<Integer> et de List<String>, vous devriez avoir un membre de type List<Integer> et un autre membre de type List<String>, et faire référence à ces membres plutôt que d'essayer d'hériter des deux. Même si vous n'aviez besoin que d'une liste d'entiers, il est préférable d'utiliser composition/délégation plutôt que l'héritage dans ce cas.

11

Le problème "classique" de Java affecte également mon développement Android ...
La raison semble être simple: 
Plus vous avez à utiliser de frameworks/bibliothèques, plus facilement les choses peuvent être hors de contrôle ...

Dans mon cas, j'ai unBootStrapperAppclass hérité deAndroid.app.Application,
alors que la même classe devrait également implémenter une interfacePlatformd'un framework MVVM afin de s'intégrer.
Une collision de méthodes s'est produite sur une méthodegetString (), qui est annoncée par les deux interfaces et doit avoir une implémentation differenet dans des contextes différents.
La solution de contournement (moche..IMO) utilise une classe interne pour implémenter toutes les méthodes Platform, uniquement à cause d'un conflit mineur de signature de méthode ... dans certains cas, une telle méthode empruntée n'est même pas utilisée (mais affecte la sémantique majeure de la conception).
J'ai tendance à penser que l'indication de contexte/espace de nom explicite de style C # est utile.

1
tiancheng

La seule solution qui me soit venue à l’esprit est d’utiliser des objets de référence à celui que vous voulez implémenter dans plusieurs interfaces.

exemple: si vous avez 2 interfaces à implémenter

public interface Framework1Interface {

    void method(Object o);
}

et 

public interface Framework2Interface {
    void method(Object o);
}

vous pouvez les inclure dans deux objets Facador:

public class Facador1 implements Framework1Interface {

    private final ObjectToUse reference;

    public static Framework1Interface Create(ObjectToUse ref) {
        return new Facador1(ref);
    }

    private Facador1(ObjectToUse refObject) {
        this.reference = refObject;
    }

    @Override
    public boolean equals(Object obj) {
        if (obj instanceof Framework1Interface) {
            return this == obj;
        } else if (obj instanceof ObjectToUse) {
            return reference == obj;
        }
        return super.equals(obj);
    }

    @Override
    public void method(Object o) {
        reference.methodForFrameWork1(o);
    }
}

et

public class Facador2 implements Framework2Interface {

    private final ObjectToUse reference;

    public static Framework2Interface Create(ObjectToUse ref) {
        return new Facador2(ref);
    }

    private Facador2(ObjectToUse refObject) {
        this.reference = refObject;
    }

    @Override
    public boolean equals(Object obj) {
        if (obj instanceof Framework2Interface) {
            return this == obj;
        } else if (obj instanceof ObjectToUse) {
            return reference == obj;
        }
        return super.equals(obj);
    }

    @Override
    public void method(Object o) {
        reference.methodForFrameWork2(o);
    }
}

À la fin, le cours que vous vouliez devrait ressembler à quelque chose comme:

public class ObjectToUse {

    private Framework1Interface facFramework1Interface;
    private Framework2Interface facFramework2Interface;

    public ObjectToUse() {
    }

    public Framework1Interface getAsFramework1Interface() {
        if (facFramework1Interface == null) {
            facFramework1Interface = Facador1.Create(this);
        }
        return facFramework1Interface;
    }

    public Framework2Interface getAsFramework2Interface() {
        if (facFramework2Interface == null) {
            facFramework2Interface = Facador2.Create(this);
        }
        return facFramework2Interface;
    }

    public void methodForFrameWork1(Object o) {
    }

    public void methodForFrameWork2(Object o) {
    }
}

vous pouvez maintenant utiliser les méthodes getAs * pour "exposer" votre classe 

1
notAtAll

Vous pouvez utiliser un modèle d’adaptateur pour que cela fonctionne. Créez deux adaptateurs pour chaque interface et utilisez-les. Cela devrait résoudre le problème.

0
AlexCon