我正在设计一个事件驱动的系统,并且遇到了一些关于泛型的基本API问题。
我喜欢扩展BaseEvent
的所有事件:
// Groovy pseudo-code
abstract BaseEvent {
Date occurredOn
BaseEvent() {
super()
this.occurredOn = new Date() // Now
}
}
我希望所有事件监听器都能实现一些基础接口:
interface EventListener<EVENT extends BaseEvent> {
void onEvent(EVENT event)
}
因此,这对于仅处理单一类型事件的简单侦听器非常有用:
class FizzEvent extends BaseEvent { ... }
class FizzEventListener implements EventListener<FizzEvent> {
@Override
void onEvent(FizzEvent fizzEvent) {
...
}
}
但我会有一些需要处理多种类型事件的听众:
class BuzzEvent extends BaseEvent { ... }
// So then, ideally:
class ComplexListener implements EventListener<FizzEvent>,
EventListener<BuzzEvent> {
@Override
void onEvent(FizzEvent fizzEvent) {
...
}
@Override
void onEvent(BuzzEvent buzzEvent) {
...
}
}
但这会产生编译器错误:
名称冲突:EventListener类型的onEvent(EVENT)方法与EventListener类型的onEvent(EVENT)具有相同的擦除,但不会覆盖它
任何想法解决处理多个事件的方法是什么?
答案 0 :(得分:4)
您遇到的问题称为 Type Erasure ,这就是Java实现泛型的方式。这意味着,对于Java,以下代码行:
@Override
void onEvent(FizzEvent fizzEvent) {
...
}
@Override
void onEvent(BuzzEvent buzzEvent) {
...
}
真的看起来像这样:
@Override
void onEvent(BaseEvent fizzEvent) {
...
}
@Override
void onEvent(BaseEvent buzzEvent) {
...
}
请注意,类型信息已被删除&#39;并且只有超类型BaseEvent
仍然是两个方法的类型参数,这会导致歧义并且不会起作用。
如果未使用extends
关键字,则只会看到Object
,但仍会遇到同样的问题。
这与C#形成对比,C#使用 Type Reification 来实现泛型,并且可以在运行时知道类型的差异。
换句话说,如果你问Java List<Dog>
是否与List<Car>
是同一种列表,Java会说&#34;是&#34;因为它在运行时并不知道更好,而C#会说&#34; no&#34;因为它保留了类型信息。
任何想法解决处理多个事件的方法是什么?
如果要使用相同的侦听器接口(例如onDogBarkEvent(Dog d)
,onCatMeowEvent(Cat c)
,或者可能为不同类型的事件创建单独的侦听器接口,则需要使用不同的方法名称或签名(例如{{ 1}},DogBarkListener
)。
这应该指向正确的方向,并提供一些Java选项。
除此之外,如果你真的对你的选择非常感兴趣和也可以自由选择你的编程语言,那么你可以考虑使用C#进行旋转,看看它是否适合你。< / p>
答案 1 :(得分:0)
一种可能的解决方案是跳过泛型并明确提供支持&#34;方法:
public FooListener implements Listener {
public <T extends BaseEvent> boolean supports(Class<T> clazz) {
//decide
}
public void handle(BaseEvent baseEvent) {
//handle
}
}
这个,结合了一些抽象类和泛型的简单&#34;简单&#34;案件,应该做的伎俩:
private Class<S> clazz;
public Class<S> getClazz() {
if(clazz==null) {
ParameterizedType superclass =
(ParameterizedType)getClass().getGenericSuperclass();
clazz = (Class<S>) superclass.getActualTypeArguments()[0];
}
return clazz;
}
public boolean supports(Class clazz) {
return clazz!=null && clazz == getClazz();
答案 2 :(得分:0)
在java 8中
public class ComplexListener
{
public final EventListener<FizzEvent> fizzListener = fizzEvent ->
{
...
}
...
每当需要complexListener.fizzListener
时使用EventListener<FizzEvent>
。
(没有java8,你可以使用匿名类获得相同的效果,只是更详细。)
java8中的另一种方法是通过方法引用
public class ComplexListener
{
public void handleFizzEvent(FizzEvent fizzListener)
{
...
}
每当需要complexListener::handleFizzEvent
时使用EventListener<FizzEvent>
。
在java泛型中,明确禁止对象可以是Foo<A>
和Foo<B>
(A!= B);即Foo<A>
和Foo<B>
是互斥的。可以提出许多原因,但我认为最重要的原因是捕获转换 - 给定Foo<?>
对象,编译器假定它是唯一Foo<X>
的{{1}}。因此,任何对象都不能X
(无论具体化)。