[英]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)
或者可能為不同類型的事件創建單獨的偵聽器接口(例如DogBarkListener
, CatMeowListener
),則需要使用不同的方法名稱或簽名。
這應該通過一些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<?>
對象,編譯器假定它是唯一X
的Foo<X>
。 因此,沒有對象可以是Foo<A> & Foo<B>
(與版本無關)。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.