使用不同的泛型类型实现同一个泛型Java接口的多个实例?
我正在设计一个事件驱动的系统,并遇到一些关于泛型的基本API问题。使用不同的泛型类型实现同一个泛型Java接口的多个实例?
我woud喜欢所有的活动延长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(事件)有相同的擦除为的onEvent(EVENT )的类型EventListener但不覆盖它
任何想法是什么解决方案是处理多个事件?
您遇到的问题被称为类型擦除,这是Java如何实现泛型。这意味着,对于Java的,下面的代码行:
@Override
void onEvent(FizzEvent fizzEvent) {
...
}
@Override
void onEvent(BuzzEvent buzzEvent) {
...
}
真的这个样子的:
@Override
void onEvent(BaseEvent fizzEvent) {
...
}
@Override
void onEvent(BaseEvent buzzEvent) {
...
}
注意类型信息已经被“抹去”,只有超类型BaseEvent
仍然作为这两种方法的类型参数,这会导致歧义并且不起作用。
如果尚未使用extends
关键字,则只会看到Object
,但仍会遇到同样的问题。
这与C#相反,它使用类型实体来实现泛型,并且可以在运行时知道类型的差异。换句话说,如果你问Java是否List<Dog>
与List<Car>
是同一种列表,Java会说“是”,因为它在运行时并不知道更好,而C#会说“不”,因为它保留了类型信息。
任何想法是什么解决方案是处理多个事件?
您将需要使用不同的方法名称或签名,如果你想使用相同的监听器接口(例如onDogBarkEvent(Dog d)
,onCatMeowEvent(Cat c)
或可能创建不同类型的事件单独的侦听器接口(例如DogBarkListener
,CatMeowListener
)。
这应该指出你有一些Java选项的正确方向。除此之外,如果你真的对你的选择有强烈的感觉和也可以自由选择你的编程语言,那么你可以考虑让C#进行旋转,看看它是否对你更好。
一个可能的解决办法是跳过泛型和有一个明确的“支持”的方法:
public FooListener implements Listener {
public <T extends BaseEvent> boolean supports(Class<T> clazz) {
//decide
}
public void handle(BaseEvent baseEvent) {
//handle
}
}
此,结合一些抽象类与仿制药的“简单”的情况下,应该做的伎俩:
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();
在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>
的唯一X
。因此没有任何对象可以是Foo<A> & Foo<B>
(不论具体化)。