Можете ли вы привести объект к интерфейсу, который он явно не реализует? - PullRequest
4 голосов
/ 15 октября 2019

Я особенно заинтересован в реализации шаблона стратегии , где одна (или более) из стратегий поступает из сторонней библиотеки.

У меня есть сторонняя библиотека стакой класс:

public class LibraryClass {
    public void doSomething() {
        // ...
    }
}

Затем я создал свой собственный класс с таким же интерфейсом:

public class MyClass {
    public void doSomething() {
        // ...
    }
}

Теперь в своем коде я хочу сделать следующее:

public class MainActivity extends AppCompatActivity {
    public interface Strategy {
        void doSomething();
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        Strategy oneClass = (Strategy)(new MyClass());
        Strategy twoClass = (Strategy)(new LibraryClass());
        oneClass.doSomething();
        twoClass.doSomething();
    }
}

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

java.lang.ClassCastException: MyClass cannot be cast to MainActivity$Controls

Наихудший сценарий, который я знаю, я могу использовать Proxy pattern , чтобы обернуть LibraryClass в нечто, что действительно реализует интерфейс Strategy:

public class LibraryClassProxy implements Strategy {
    private LibraryClass internal;
    public void LibraryClassProxy() {
        internal = new LibraryClass();
    }
    public void doSomething() {
        internal.doSomething();
    }
}

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

Ответы [ 3 ]

1 голос
/ 15 октября 2019

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

Вы не можете делать такую ​​типизацию утки в Java; все отношения типа должны быть явными. Но с помощью лямбды и преобразования SAM вы можете сделать что-то, что довольно близко к этому.

public class MainActivity extends AppCompatActivity {
    public interface Strategy {
        void doSomething();
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        Strategy oneClass = new MyClass()::doSomething;
        Strategy twoClass = new LibraryClass()::doSomething;

        oneClass.doSomething();
        twoClass.doSomething();
    }
}

Вместо того, чтобы пытаться разыгрывать (что вы не можете), создайте новый Strategy объекты, использующие doSomething методы объектов, которые вы хотите. Лично это похоже на грязный хак, но эй, это то, что ты хотел.

1 голос
/ 15 октября 2019

Как уже указывали другие, в определениях Java-интерфейсов используются явные объявления. Я полагаю, что описываемая вами ситуация охватывается шаблоном проектирования Adapter , а не Proxy:

Intent: Преобразование интерфейса класса в другойинтерфейс клиенты ожидают. Адаптер позволяет классам работать вместе, что иначе невозможно из-за несовместимых интерфейсов. --- GoF р. 139

И решение «Прокси», на которое вы указываете, соответствует варианту object шаблона Adapter. Если LibraryClass не является окончательным, можно рассмотреть вариант использования class варианта шаблона:

class MyLibraryClass extends LibraryClass implements Strategy {
   /* ... */
}
1 голос
/ 15 октября 2019

С Java Docs :

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

Итак, вы можете 'приведение объекта к интерфейсу, который явно не реализован

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