簡體   English   中英

用不同的泛型類型實現同一泛型Java接口的多個實例?

[英]Implementing multiple instances of the same generic Java interface with different generic types?

我正在設計一個事件驅動的系統,並且遇到了有關泛型的一些基本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)相同的擦除,但不會覆蓋它

有什么想法可以解決多個事件嗎?

您遇到的問題稱為Type Erasure ,這是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#相反,C#使用Type Reification實現泛型並且可以在運行時知道類型的差異。

換句話說,如果您問Java List<Dog>是否與List<Car>屬於相同種類的列表,則Java會說“是”,因為它在運行時並不知道更好,而C#會說“否” ”,因為它保留類型信息。

有什么想法可以解決多個事件嗎?

如果要使用相同的偵聽器接口(例如onDogBarkEvent(Dog d)onCatMeowEvent(Cat c)或者可能為不同類型的事件創建單獨的偵聽器接口(例如DogBarkListenerCatMeowListener ),則需要使用不同的方法名稱或簽名。

這應該通過一些Java選項為您指明正確的方向。

除此之外,如果您真的對選擇感到強烈並且還可以自由選擇編程語言,那么您可以考慮試用C#,看看它是否對您更好。

一個可能的解決方案是跳過泛型,並使用顯式的“ supports”方法:

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)
    {
        ...
    }

每當需要EventListener<FizzEvent>時,請使用complexListener::handleFizzEvent


在Java泛型中,明確禁止對象既可以是Foo<A>也可以是Foo<B> (A!= B); Foo<A>Foo<B>是互斥的。 可能有很多原因,但是我認為最重要的原因是由於捕獲轉換-給定Foo<?>對象,編譯器假定它是唯一XFoo<X> 因此,沒有對象可以是Foo<A> & Foo<B> (與版本無關)。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM