Использование параметра, который реализует несколько интерфейсов pre-generics - PullRequest
33 голосов
/ 24 июня 2010

Предположим, у меня есть эти интерфейсы:

public interface I1 {
  void foo();
}

public interface I2 {
  void bar();
}

и классы:

public class A extends AParent implements I1, I2 {
   // code for foo and bar methods here
}

public class B extends BParent implements I1, I2 {
  // code for foo and bar methods here
}

public class C extends CParent implements I1 {
  // code for foo method here
}

Теперь с дженериками у меня может быть такой метод:

public <T extends I1 & I2> void method(T param) {
  param.foo();
  param.bar();
}

и я могу назвать его как A и B в качестве параметров, но не с C (он не реализует I2).

Был ли способ достижения этого типа предварительных обобщений безопасности типов (java <1.5). </p>

Учтите, что A, B и C имеют разные деревья наследования, и на самом деле нельзя делать что-то вроде AParent и BParent, имеющих самих общих родителей.

Я знаю, что вы могли бы сделать:

public void method(I1 param) {
  param.foo();
  ((I2)param).bar();
}

но тогда вы могли бы также позвонить method(new C()), который не реализует I2, так что вы попали в беду.

Так есть ли другие способы, которыми вы могли бы сделать это?

P.S. Мне действительно не нужно этого делать, я спрашиваю, в основном, из любопытства.

Ответы [ 4 ]

25 голосов
/ 24 июня 2010

Создание третьего интерфейса I3 расширяет I1 и I2. Тогда класс A и B оба реализуют I3, а универсальный метод принимает I3.

Это, пожалуй, единственный способ сделать это.

12 голосов
/ 30 апреля 2016

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

1. make one interface расширяет оба интерфейса:

public interface IC extends IA,IB {
   // empty method here
}

Выше код не имеет смысла. Вы определяете отдельный интерфейс только для объединения других интерфейсов, и внутри нет никаких новых методов. Вы не добавляете значение к IC вместо объединения двух интерфейсов IA и IB. И в некоторых ситуациях этот код сделает ваш код «немного веселым», когда вы не можете найти подходящее имя для третьего интерфейса. Эта ситуация приведет к некоторому имени интерфейса, например IReadableAndWriteable или ISomethingAndSomethingAndAnotherThing

2. метод литья внутри метода:

public void methodA(IA object) {
   if (object instance of IB) {
     ((IB)(object)).someMethod()
}

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

Истинное решение:

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

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

public void exampleMethod(IA object1, IB object2) {
   object1.someMethod()
   object2.someMethod()
}

И для вызова вышеуказанного метода вы помещаете один и тот же параметр внутрь (если программист использует один и тот же объект). Они также могут помещать разные предметы.

public void caller() {
   IC object3 = new C(); // C is the class implements both IA and IB
   exampleMethod(object3, object3);
   IA objectA = new A();
   IB objectB = new B();
   exampleMethod(objectA, objectB);       
}

Надеюсь, эта помощь:)

2 голосов
/ 24 июня 2010

sri - лучший ответ, если у вас было разрешение изменить подпись A и B. Однако, если у вас не было разрешения, вы могли бы сделать:

public void method(I1 param1 , I2 param2) { // unpopular classes that do not implement I3 must use this method
  param1.foo();
  param2.bar();
}
public void method(I3 param){ // I hope everybody implements I3 when appropriate
  param.foo();
  param.bar();
}
public void method(A param){// A is a popular class
  method(param,param);
}
public void method(B param){// B is a popular class
  method(param,param);
}

Конечно, теперь просто используйте дженерики.

2 голосов
/ 24 июня 2010

До Java 1.5 не было решения IMO для достижения такой безопасности типов во время компиляции. Но во время выполнения есть душа, использующая instanceof.

public void method(Object o) {
  if (!(o instanceof I1))
    throw new RuntimeException("o is not instance of I1");

  if (!(o instanceof I2))
    throw new RuntimeException("o is not instance of I2");

  // go ahead ...
}
...