Реализация общего интерфейса в Java - PullRequest
27 голосов
/ 01 декабря 2010

У меня есть вопрос об Java-дженериках, на который, я надеялся, кто-нибудь может ответить. Рассмотрим следующий код:

public interface Event{}
public class AddressChanged implements Event{}
public class AddressDiscarded implements Event{}

public interface Handles<T extends Event>{
    public void handle(T event);
}

Я хочу реализовать этот интерфейс Handles следующим образом:

public class AddressHandler implements Handles<AddressChanged>, Handles<AddressDiscarded>{
    public void handle(AddressChanged e){}
    public void handle(AddressDiscarded e){}
}

Но java не позволяет реализовывать Handles дважды, используя Generic. Мне удалось сделать это с C #, но я не могу найти обходной путь в Java без использования Reflection или instanceof и приведения.

Есть ли способ в java для реализации интерфейса Handles, используя оба общих интерфейса? Или, может быть, другой способ написания интерфейса Handles для достижения конечного результата?

Ответы [ 7 ]

18 голосов
/ 01 декабря 2010

После @ Амир Раминфар вы можете использовать посетитель шаблон

interface Event{
 void accept(Visitor v);
}
interface Visitor {
 void visitAddressChanged(AddressChanged a);
 void visitAddressDiscarded(AddressDiscarded a);
}

class AddressChanged implements Event{
 @Override
 public void accept(Visitor v) {
  v.visitAddressChanged(this);
 } 
}

class AddressDiscarded implements Event{
 @Override
 public void accept(Visitor v) {
  v.visitAddressDiscarded(this);
 } 
}

class AddressHandler implements Visitor {
    void handle(Event e){
       e.accept(this);
     }
    public void visitAddressChanged(AddressChanged e){}
    public void visitAddressDiscarded(AddressDiscarded e){}
}
10 голосов
/ 01 декабря 2010

Вы не можете сделать это в Java.Вы можете реализовать только одну конкретную реализацию одного и того же универсального интерфейса.Я бы сделал это вместо:

public class AddressHandler implements Handles<Event>{
    public void handle(Event e){
      if(e instanceof AddressDiscarded){
         handleDiscarded(e);
      } else if(e instanceof AddressChanged){
         handleChanged(e);
      }
    }
    public void handleDiscarded(AddressDiscarded e){}
    public void handleChanged(AddressChanged e){}
}
2 голосов
/ 01 декабря 2010

Нет, потому что разные "конкретные" универсальные типы в Java компилируются в одного типа . Фактический интерфейс, который будет реализован вашим объектом:

public interface Handles {
    public void handle(Event event);
}

И, очевидно, у вас не может быть двух разных методов с одинаковой подписью ...

1 голос
/ 01 декабря 2010

AFAIK вы не можете этого сделать, потому что при компиляции исходного кода в Java они оба сводятся к handle(Event), что делает метод неоднозначным.

Общая информация недоступна во время выполнения в Java, вв отличие от C #.Вот почему это работает так, как вы описали.

Вам придется изменить имена методов, чтобы сделать их уникальными, например handleAddressChanged и handleAddressDiscarded.

Это действительно один изслабые места дженериков Java.

0 голосов
/ 04 декабря 2010

Такая реализация не будет работать из-за ограничений спецификации Java. Но если вы не боитесь использовать AOP или какой-нибудь IOC-контейнер, вы можете использовать для этого аннотации. Чем ваши Аспекты или контейнер могут управлять инфраструктурой обмена сообщениями и вызывать методы, которые вы аннотируете.

Сначала необходимо создать аннотации.

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface EventConsumer {}

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Handles{}

Вы можете комментировать свой класс следующим образом:

@EventConsumer
public class AddressHandler{
    @Handles
    public void handle(AddressChanged e){}
    @Handles
    public void handle(AddressDiscarded e){}
}
0 голосов
/ 01 декабря 2010

Это не разрешено, потому что Java стирает общие подписи во время компиляции.Метод интерфейса на самом деле будет иметь подпись

public void handle(Object event);

Таким образом, у вас есть два варианта.Либо реализуйте отдельные обработчики для разных событий:

public class AddressChangedHandler implements Handles<AddressChanged>{ /* ... */ }
public class AddressDiscardedHandler implements Handles<AddressDiscarded>{ /* ... */ }

, либо реализуйте один обработчик для всех, кроме проверки типа входящего события:

public void handle(Event e){
  if (e instanceof AddressChanged) {
     handleAdressChanged(e);
  }
  else if (e instanceof AddressDiscareded) {
     handleAdressDiscarded(e);
  }
}
0 голосов
/ 01 декабря 2010

К сожалению нет.Обычное решение (толстое, уродливое, быстрое) - создать один Handles интерфейс (т.е. HandlesAddressChange, HandlesAddressDiscarded) и назначить каждому из них свой метод (handleAddressChange(...), handleAddressDiscarded()).

Таким образом, среда выполнения Java может различать их.

Или вы можете использовать анонимные классы.

...