реализация интерфейсов после факта - PullRequest
5 голосов
/ 15 апреля 2009

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

Предположим, у нас есть класс C, который уже используется в скомпилированном коде. (Мы не можем ни изменить этот код, ни первоначальное определение C).

Предположим, далее есть интересный код, который можно использовать повторно, если бы только C реализовывал интерфейс I. На самом деле, более или менее тривиально получить D, который является просто C + реализацией методов интерфейса.

Тем не менее, кажется, что, когда у меня есть C, нет никакого способа сказать: я хочу, чтобы вы были D, то есть C, реализующим I.

(Дополнительное замечание: я думаю, что приведение (D) c, где тип времени выполнения c - C, должно быть разрешено, если D - это C, а единственным отличием от C являются добавленные методы. Это должно быть безопасно, не так ли? )

Как можно обойти это бедствие?

(Я знаю шаблон проектирования фабрики, но, похоже, это не решение. Ведь, как только нам удастся создать D во всех местах, где раньше были C, кто-то найдет другой интерфейс J полезным и получит E, расширяющий C реализует J. Но E и D несовместимы, поскольку они оба добавляют различный набор методов к C. Поэтому, хотя мы всегда можем передать E, где ожидается C, мы не можем передать E, где ожидается D. Теперь нам нужен новый класс F, расширяющий C, реализует I, J.)

Ответы [ 6 ]

10 голосов
/ 15 апреля 2009

Не могли бы вы использовать класс делегата, т. Е. Новый класс, который оборачивает экземпляр «Класса C», но также реализует «Интерфейс I»?

public class D implements I {

    private C c;

    public D (C _c) {
        this.c = _c;
    }

    public void method_from_class_C() {
        c.method_from_class_C();
    }
    // repeat ad-nauseum for all of class C's public methods
    ...

    public void method_from_interface_I() {
        // does stuff
    }
    // and do the same for all of interface I's methods too
}

и затем, если вам нужно вызвать функцию, которая обычно принимает параметр типа I, просто сделайте это:

result = some_function(new D(c));
7 голосов
/ 15 апреля 2009

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

Если вам нужна аналогичная совместимость во время выполнения с классами, я предлагаю вам взглянуть на библиотеки cglib или javaassist с открытым исходным кодом.

3 голосов
/ 15 апреля 2009

Если вы (можете) управлять ClassLoader, который загружает ваш класс C, тогда вы можете попытаться выполнить некоторые махинации времени загрузки класса с инструментарием байт-кода, чтобы класс реализовал интерфейс.

То же самое можно сделать во время сборки, конечно. Это может даже быть проще (так как вам не нужен доступ к ClassLoader).

2 голосов
/ 15 апреля 2009

(Примечание: я думаю, что актерский состав (D) c, где тип времени выполнения c - C, должен быть допускается, если D является C и единственным Разница в C добавлены методы. Это должно быть безопасно, не так ли?)

Совсем нет. Если бы вы могли сделать это приведение, то вы могли бы скомпилировать код, который пытался вызвать один из «добавленных методов» для этого объекта, который потерпел бы неудачу во время выполнения, так как этот метод не существует в C.

Я думаю, вы представляете, что приведение в действие обнаружит методы, которые "отсутствуют" в C, и автоматически делегирует их D. Я сомневаюсь, что это было бы возможно, хотя я не могу говорить о последствиях языкового дизайна.

Мне кажется, решение вашей проблемы:

Определить класс D, который расширяет C и реализует I
Определите конструктор D (C c), который по существу клонирует состояние данного объекта C в новый объект D.
Объект D может быть передан в существующий код, потому что это C, и он может быть передан в код, который хочет I, потому что это I

1 голос
/ 15 апреля 2009

Я полагаю, что вы хотите, используя java.lang.reflect.Proxy; на самом деле я сделал нечто подобное для текущего проекта. Однако это довольно трудоемкая работа, и полученные в результате «гибридные объекты» могут демонстрировать странное поведение (поскольку вызовы методов для них направляются к различным конкретным объектам, возникают проблемы, когда эти методы пытаются вызывать друг друга).

0 голосов
/ 16 апреля 2009

Я думаю, вы не можете этого сделать, потому что Java строго типизирован. Я полагаю, что это может быть сделано на таких языках, как Ruby и Python, с использованием mixins.

Что касается Java, это определенно выглядит как хорошее использование для шаблона проектирования адаптера (он уже был предложен ранее как объект-оболочка).

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