Подтвердить что ты не робот

Внедрение универсального интерфейса в Java

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

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 не позволяет дважды вводить дескрипторы с помощью Generic. Я смог выполнить это с помощью С#, но не могу определить обходной путь в java без использования Reflection или instanceof и casting.

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

4b9b3361

Ответ 1

Вы не можете сделать это на 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

Следуя @Amir Raminfar, вы можете использовать посетитель шаблон

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){}
}

Ответ 3

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

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

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

Ответ 4

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

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

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

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

Ответ 5

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

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

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

Ответ 6

Это не разрешено, потому что 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);
  }
}

Ответ 7

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

Сначала вам нужно создать аннотации.

@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){}
}