我有一个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不允许使用Generic两次实现Handles。我能够使用C#完成此任务,但是如果不使用Reflection或instanceof并进行转换,则无法在java中找到解决方法。
java中有没有办法使用两个通用接口实现Handles接口?或者也许是另一种编写Handles接口的方法,以便完成最终结果?
答案 0 :(得分:18)
在@Amir Raminfar之后,您可以使用visitor 模式
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){}
}
答案 1 :(得分:10)
你不能用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 :(得分:2)
不,因为Java中不同的“具体”泛型类型编译为相同的类型。您的对象将实现的实际接口是:
public interface Handles {
public void handle(Event event);
}
而且,显然,你不能有两种不同的方法具有相同的签名......
答案 3 :(得分:1)
AFAIK你不能那样做,因为在用Java编译源代码时,这些都会归结为handle(Event)
,使方法不明确。
与C#相比,Java中的运行时期间无法使用通用信息。这就是为什么它如你所描述的那样有效。
您必须更改方法名称才能使其唯一,例如handleAddressChanged
和handleAddressDiscarded
。
这确实是Java泛型的弱点之一。
答案 4 :(得分:0)
不幸的是没有。通常的解决方案(胖,丑,快)是创建一个Handles
接口(即HandlesAddressChange
,HandlesAddressDiscarded
)并为每个接口提供不同的方法(handleAddressChange(...)
,{ {1}})。
这样,Java运行时可以区分它们。
或者您可以使用匿名类。
答案 5 :(得分:0)
不允许这样做,因为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);
}
}
答案 6 :(得分:0)
由于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){}
}
答案 7 :(得分:0)
如果您不介意使用(小型)库,这里是我写的可以解决您问题的库:
https://github.com/bertilmuth/requirementsascode
你会构建一个这样的模型
Model.builder()
.on(AddressChanged.class).system(this::handleAddressChanged)
.on(AddressDiscarded.class).system(this::handleAddressDiscarded)
.build()
并运行它。
网站上详细描述了如何做到这一点。