2014-02-20 1 views
1

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

Давайте предположим, что мы имеем следующее:

public interface A { 
    public void a(); 
} 

и

public class B implements A { 
    @override 
    public void a() { 
     System.out.println("a"); 
    } 
} 

Теперь я хочу, чтобы создать класс C, который также implements A и принимает другую случайную реализацию A:

public class C implements A { 
     public C(A a) { 
      //what do I need to do with a here? 
     } 
     public void c() { 
      System.out.println("c"); 
     } 
    } 

Теперь, если у меня есть foll :

A b = new B(); 
A c = new C(b); 
c.a(); 

Выход должен быть «а».

Я не могу просто

public class C extends B { 
... 

, как C, как предполагается, чтобы иметь возможность работать с любой реализацией A, а не только В.

Я также не могу

public class C implements A { 
     private a; 
     public C(A a) { 
      this.a = a; 
     } 
     @override 
     public void a() { 
      a.a(); 
     } 
     public void c() { 
      System.out.println("c"); 
     } 
    } 

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

Есть ли способ справиться с этой проблемой в Java?

В качестве другого примера замените A: List; B: ArrayList; C: FooList; a(): size()

+1

«поскольку это означало бы, что я должен перенаправить каждый метод интерфейса и переписать C всякий раз, когда что-то меняется с помощью A» - хорошо, если A изменяется, вы * имеете *, чтобы изменить каждую реализацию A. Это так, как есть. –

+0

Возможно ли для вас создать абстрактный класс вместо интерфейса? – Manish

+0

@ Мишень нет, см. Мой пример списка. Давайте просто подумаем, что мне нужен метод .count(), который возвращает .size() + 1. Проблема заключается в том, что ArrayList, например, не работал бы, если C ожидает AbstractFooList в качестве входного аргумента. – makrom

ответ

0

К сожалению, в Java нет способа сделать это, кроме вашего последнего фрагмента кода. Однако различные IDE помогут вам в генерации кода и, пометив все методы, @override будет означать, что вы получите предупреждение или сообщение об ошибке, если ваша реализация C точно не соответствует интерфейсу A.

Для Eclipse (и, по-видимому, IntelliJ) см. Команду «Создать методы делегатов».

+0

Спасибо, это то, чего я боялся. Мое последнее предложение было бы чрезвычайно сложным, так как было бы так много частей, где любые изменения в интерфейсе потребуют переписывания кода. Подумайте о прекомпилированных библиотеках, содержащих C. Таким образом, любое изменение в A также потребует изменения C. – makrom

+0

Как правило, интерфейсы должны меняться * очень редко *, поэтому, надеюсь, вам не придется часто это делать. Но когда вы это делаете, вы можете просто «выбрать все», «удалить» и «генерировать методы делегатов». – Sneftel

+0

Я не думаю, что фактическая обработка в среде IDE была бы большой проблемой, это скорее нарушение ответственности, которое меня беспокоит. – makrom

0

Это, вероятно, не поможет вам сразу же, но если вы использовали Java 8, вы можете решить это с помощью методов защитников, которые являются методами, реализованными в интерфейсе.

Тогда вы бы использовали для каждого существующего класса реализации свой класс, который extends класс и implements ваш дополнительный интерфейс с методами защитника. Методы будут «смешаны» с вашим классом.

Java 8 не за горами, однако, это не далекое решение. Oracle пообещала, что выпустит его к концу этого квартала, что означает менее чем через полтора месяца.

+0

Позвольте мне ответить вот так: «Java 7? Что это?» Но спасибо за замечание, звучит круто, и я посмотрю на это из личного интереса. – makrom

2

Что вы ищете, это динамический прокси-сервер, который автоматически реализует все методы интерфейса, делегируя конкретную реализацию этого интерфейса. Это не тривиально, но не так сложно сделать, используя класс Java Proxy.

Конкретный пример такой прокси, который «добавляет» методы к любому экземпляру PreparedStatement, окружив его, можно найти на https://github.com/Ninja-Squad/ninja-core/blob/master/src/main/java/com/ninja_squad/core/jdbc/PreparedStatements.java

+0

Спасибо, я думаю, что это может быть осуществимый подход, хотя это довольно сложно, чем я надеялся. – makrom

0

Есть ли способ, чтобы справиться с этой проблемой в Java?

В принципе, нет.

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


Другой альтернативой было бы использовать прокси-класс ... который будет эффективно генерировать динамический прокси. Проблема в том, что для этого требуется InvocationHandler, который (я думаю) использовал бы отражение, чтобы сделать вызов обернутого объекта. Это сложно и не будет эффективным.

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


Было бы также можно сгенерировать класс-обертку C из ничего, используя библиотеку BCEL или аналогичный. Но это еще хуже идея (ИМО).

+0

Я бы не стал быстро отбрасывать динамические прокси. В конце концов, они являются основой AOP Spring, например, используются в тысячах реальных приложений. –

+0

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

Смежные вопросы