Внедрение универсального интерфейса в 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, чтобы конечный результат мог быть достигнут?
Ответы
Ответ 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){}
}