Java - вызов имени метода при реализации интерфейса

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

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

Ответ 1

Нет, нет способа реализовать один и тот же метод двумя разными способами в одном классе 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();}
}

Ответ 2

Нет реального способа решить эту проблему на 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, downcasts обычно являются злыми, и если можно ожидать, что экземпляр Alfa часто имеет аспект Beta тоже, а для некоторых Причина (обычно оптимизация - единственная действительная причина), которую вы хотите преобразовать в Beta, вы можете сделать суб-интерфейс Alfa с Beta asBeta() в нем.

Ответ 3

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

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

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

Ответ 4

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

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

Ответ 5

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

например: предположим, что у вас есть 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 * для "раскрытия" вашего класса

Ответ 6

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

Ответ 7

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

public class MyClass{

    private String name;

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

    public String getName(){
        return name;
    }
}

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

В С# это будет trvial

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, не говоря уже о переопределении по умолчанию.... потому что конечно, каждый компонент, предназначенный для дорожного автомобиля, должен быть способен захлопнуться в летающем автомобиле и просто работать - эй они оба автомобиля... Я уверен, что функциональность по умолчанию, скажем, на вашем спутниковом навигаторе, не будет влиять на входные и стартовые входы по умолчанию, потому что автомобили только рыскают!