Java - столкновение имени метода в реализации интерфейса - PullRequest
85 голосов
/ 08 апреля 2010

Если у меня есть два интерфейса, оба совершенно разные по своему назначению, но с одинаковой сигнатурой метода, как заставить класс реализовать оба без необходимости писать один метод, который служит для обоих интерфейсов, и писать некоторую запутанную логику в реализации метода, которая проверяет, для какого типа объекта выполняется вызов, и вызывает соответствующий код?

В C # это преодолевается тем, что называется явной реализацией интерфейса. Есть ли эквивалентный способ в Java?

Ответы [ 7 ]

72 голосов
/ 08 апреля 2010

Нет, нет способа реализовать один и тот же метод двумя разными способами в одном классе в Java.

Это может привести ко многим запутанным ситуациям, поэтому Java это запретила.

interface ISomething {
    void doSomething();
}

interface ISomething2 {
    void doSomething();
}

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

Что вы можете сделать, это составить класс из двух классов, каждый из которых реализует свой интерфейс. Тогда этот один класс будет иметь поведение обоих интерфейсов.

class CompositeClass {
    ISomething class1;
    ISomething2 class2;
    void doSomething1(){class1.doSomething();}
    void doSomething2(){class2.doSomething();}
}
13 голосов
/ 08 апреля 2010

Нет реального способа решить эту проблему на Java. Вы можете использовать внутренние классы в качестве обходного пути:

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()
        };
    }
}

Несмотря на то, что он не допускает приведение от AlfaBeta до Beta, откаты обычно являются злом, и если можно ожидать, что экземпляр Alfa часто имеет аспект Beta, а для некоторых причина (обычно оптимизация - единственная действительная причина), вы хотите иметь возможность преобразовать ее в Beta, вы можете создать подчиненный интерфейс Alfa с Beta asBeta() в нем.

11 голосов
/ 08 апреля 2010

Если вы столкнулись с этой проблемой, скорее всего, вы используете наследование там, где вам следует использовать делегирование .Если вам необходимо предоставить два разных, хотя и похожих, интерфейса для одной и той же базовой модели данных, то вам следует использовать представление , чтобы дешево предоставлять доступ к данным с использованием какого-либо другого интерфейса.Чтобы привести конкретный пример для последнего случая, предположим, что вы хотите реализовать как Collection, так и MyCollection (который не наследуется от Collection и имеет несовместимый интерфейс).Вы можете предоставить функции Collection getCollectionView() и MyCollection getMyCollectionView(), которые обеспечивают облегченную реализацию Collection и MyCollection, используя те же базовые данные.

Для первого случая ... предположим, что вам действительно нужен массив целых чисел и массив строк.Вместо того чтобы наследовать от List<Integer> и List<String>, вы должны иметь один член типа List<Integer> и другой член типа List<String> и ссылаться на эти члены, а не пытаться наследовать от обоих.Даже если вам нужен только список целых чисел, в этом случае лучше использовать композицию / делегирование вместо наследования.

1 голос
/ 21 мая 2013

Единственное решение, которое мне пришло в голову, - это использование объектов-ссылок на объект, для которого вы хотите реализовать множественные интерфейсы.

Например: предположим, что у вас есть 2 интерфейса для реализации

public interface Framework1Interface {

    void method(Object o);
}

и

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

вы можете заключить их в два объекта 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);
    }
}

и

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);
    }
}

В конце концов, класс, который вы хотели, должен выглядеть примерно так:

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) {
    }
}

теперь вы можете использовать методы getAs *, чтобы "выставить" ваш класс

1 голос
/ 29 марта 2013

«Классическая» проблема Java также влияет на мою разработку Android ...
Причина кажется простой:
Чем больше фреймворков / библиотек вам нужно использовать, тем легче вещи могут выйти из-под контроля ...

В моем случае у меня есть класс BootStrapperApp , унаследованный от android.app.Application ,
тогда как тот же класс должен также реализовать Платформа интерфейс инфраструктуры MVVM для интеграции.
Произошло столкновение метода в методе getString () , который объявлен обоими интерфейсами и должен иметь разную реализацию в разных контекстах.
Обходной путь (уродливый..IMO) использует внутренний класс для реализации всех методов Platform , только из-за одного незначительного конфликта сигнатур методов ... в некоторых случаях такой заимствованный метод даже не используется вообще ( но затронул основную семантику дизайна).
Я склонен согласиться, что явное указание контекста / пространства имен в стиле C # полезно.

0 голосов
/ 29 июня 2017

Все хорошо, если у вас есть полный контроль над всем соответствующим кодом и вы можете реализовать это заранее.Теперь представьте, что у вас есть существующий открытый класс, используемый во многих местах с методом

public class MyClass{

    private String name;

    MyClass(String name){
        this.name = name;
    }

    public String getName(){
        return name;
    }
}

Теперь вам нужно передать его в готовый WizzBangProcessor, который требует классов для реализации WBPInterface ..., который также имеетgetName (), но вместо конкретной реализации этот интерфейс ожидает, что метод вернет имя типа обработки Wizz Bang.

В C # это будет тривиально

public class MyClass : WBPInterface{

    private String name;

    String WBPInterface.getName(){
        return "MyWizzBangProcessor";
    }

    MyClass(String name){
        this.name = name;
    }

    public String getName(){
        return name;
    }
}

В Java Tough вам нужно будет идентифицировать каждую точку в существующей развернутой кодовой базе, где вам нужно конвертировать из одного интерфейса в другой.Конечно, компания WizzBangProcessor должна была использовать getWizzBangProcessName (), но они тоже разработчики.В их контексте getName был в порядке.На самом деле, за исключением Java, большинство других языков на основе OO поддерживают это.Java редко заставляет все интерфейсы реализовываться одним и тем же методом NAME.

В большинстве других языков есть компилятор, который более чем рад принять инструкцию, говорящую: «этот метод в этом классе, который соответствует сигнатуре этого метода в этом реализованном интерфейсе, является его реализацией».В конце концов, весь смысл определения интерфейсов состоит в том, чтобы позволить абстрагированию определения от реализации.(Даже не начинайте, чтобы у меня были методы по умолчанию в Интерфейсах в Java, не говоря уже о переопределении по умолчанию .... потому что, конечно, каждый компонент, предназначенный для дорожного автомобиля, должен быть в состоянии попасть в летающий автомобиль и просто работать - эйони оба машины ... Я уверен, что по умолчанию функциональность скажет, что ваша спутниковая навигация не будет зависеть от ввода по умолчанию и тональности, потому что машины только рыскать!

0 голосов
/ 02 декабря 2014

Вы можете использовать шаблон адаптера, чтобы заставить их работать.Создайте два адаптера для каждого интерфейса и используйте его.Это должно решить проблему.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...