Можно ли из моего кода изменить интерфейс Java в другом пакете? - PullRequest
4 голосов
/ 08 августа 2011

Я использую (с открытым исходным кодом) API в своем проекте Java и ссылаюсь на интерфейсы из него. Один интерфейс (Foo) широко используется как в API, так и в проектах, которые его используют. Для моего проекта я хотел бы использовать интерфейс, который предоставляет только подмножество методов, которые Foo делает, так что Foo наследовал бы методы от моего нового интерфейса (Bar). Я вообще не хочу менять интерфейс Foo; скорее я хотел бы иметь возможность писать классы, которые реализуют Bar, а затем манипулировать ими вместе с классами, которые реализуют Foo, как будто они все Bar.

Поскольку API находится в другом пакете, есть ли способ сделать это так, чтобы Bar был суперклассом (интерфейсом) всех Foo?

Ответы [ 4 ]

3 голосов
/ 08 августа 2011

Я хотел бы иметь возможность писать классы, которые реализуют Bar, а затем манипулировать ими вместе с классами, которые реализуют Foo, как если бы они все были Bar.

Вы можете создатьабстрактный бар, который реализует Foo.Вы отбросите методы, совместно используемые двумя, и реализует методы, которые Bar не разделяет, поэтому они выдают исключение, например UnsupportedOperationException.

Но вы не можете манипулировать Foo, как если бы это был Bar;это наоборот.Вы не можете удовлетворить принцип замены Лискова так, как вы предлагаете.

2 голосов
/ 08 августа 2011

Вы можете использовать шаблон адаптера для выполнения этого:

public class FooAdapter implements Bar {
   private Foo wrapped;
   public FooAdapter(Foo foo) {
      wrapped = foo;
   }
   public void op1() {
      foo.op1();
   }
   public string op2(int param) {
      return foo.op2(param);
   }
}
2 голосов
/ 08 августа 2011

Возможно, вам потребуется использовать Aspect , чтобы это произошло.Похоже, AspectJ имеет @ DeclareParents , который может делать то, что вы хотите.

1 голос
/ 08 августа 2011

Поскольку в Java есть номинальная типизация, так что org.theirs.Foo никак не связан с org.yours.Foo, даже если они имеют одинаковые сигнатуры методов, я не думаю, что это возможно сделать с наследованием в Java. AFAIK не может сказать, что даже при использовании дженериков «этот метод использует экземпляр Bar<T extends Foo OR Bar>».

Вместо этого, я думаю, вы хотите использовать интерфейс адаптера для библиотеки Foo, со своей собственной (т. Е. FooAdapter, имеющей те же подписи, что и Foo), и иметь это продлить Bar. Методы, которые вы хотите в Bar, можно затем извлечь из FooAdapter. К сожалению, ваш код придется изменить так, чтобы везде вы ранее ссылались на их Foo, вместо этого вы должны ссылаться либо на:

  • Bar если вызваны только те методы, которые вы определили бы в своем интерфейсе
  • FooAdapter если вызываются методы, определенные в Foo, но НЕ в Bar.

Этот метод, хотя и чистый, но с хорошим разделением интересов, может быть болезненным и утомительным для реализации, я боюсь.

Пример того, как это будет работать, показан в следующих фрагментах кода:

Сторонняя библиотека

package org.theirs;
public interface Foo {
    void doSomething();
    void doSomethingExtra();
}

Ваш код

package org.mine;
public interface Bar {
    void doSomething();
}

public class BarImpl implements Bar{
     public void doSomething( /* implementation */ );
}

public class FooAdapter implements Bar{
    private final Foo adapted;
    public FooAdapter(Foo adapted) {
         this.adapted = adapted;
    }
    public void doSomething() {
        adapted.doSomething(); // delegate to adapted instance
    }
}

public class UsingThoseBars {
    public void doSomethingWithAllThoseBars(Collection<Bar> bars) {
         // each entry in bars could either be a BarImpl or a FooAdapter    
    }
}

Из этого примера видно, что метод doSomethingExtra() недоступен из вашего кода, поскольку интерфейс Bar не указывает его.

Другие предложения

Обратите внимание, что при переписывании классов могут быть полезны некоторые приемы, такие как AspectJ. Я предполагаю, что вы предпочли бы получить желаемый эффект во время компиляции, на чистой Java.

Другим предложением является реализация Bar, которая выдает UnsupportedOperationException для методов Foo, которые вам не нужны. Хотя в крупных библиотеках Java есть прецедент для этого (например, UnmodifiableList в JDK), я бы рекомендовал вообще отказаться от этой практики. Однако, если стоимость замены ссылок на их Foo на новые FooAdapter достаточно высока, использование этой стратегии может быть хорошим компромиссом.

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