2015-11-05 101 views
2

我正在设计一个事件驱动的系统,并遇到一些关于泛型的基本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但不覆盖它

任何想法是什么解决方案是处理多个事件?

回答

4

您遇到的问题被称为类型擦除,这是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#进行旋转,看看它是否对你更好。

0

一个可能的解决办法是跳过泛型和有一个明确的“支持”的方法:

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(); 
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>的唯一X。因此没有任何对象可以是Foo<A> & Foo<B>(不论​​具体化)。