使用不同的泛型类型实现相同通用Java接口的多个实例?

时间:2015-11-05 02:08:08

标签: java generics

我正在设计一个事件驱动的系统,并且遇到了一些关于泛型的基本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)具有相同的擦除,但不会覆盖它

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

3 个答案:

答案 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(无论具体化)。