接口中泛型的使用

3

我有一个问题,关于接口中的正确方法签名是什么,为什么。我的事件带有一个类型参数,但接口是否也应该使用 <T> 在方法签名中使用它?如果是这样,为什么?如果不是,为什么不是?

public interface MyListener {
    void beforeAction(final MyEvent event);
}

并且
public class MyEvent<T> extends EventObject {

    // code
}
2个回答

5
如果MyEvent带有类型参数,则需要将MyListener指定为以下之一:
public interface MyListener<T> {
    void beforeAction(final MyEvent<T> event);
}

如果有不特定于封闭的 MyListener 的不同类型的 MyEvents,那么:
public interface MyListener {
    <T> void beforeAction(final MyEvent<T> event);
}

或者,正如Thomas所说,你可以完全忽略T的类型:

public interface MyListener {
    void beforeAction(final MyEvent<?> event);
}

您必须执行上述操作之一,否则将会收到编译器警告,提示您正在使用原始类型。


1
或者如果在这种情况下T无关紧要,您可以使用void beforeAction(final MyEvent<?> event); - Thomas
1
你能解释一下为什么吗? - LuckyLuke
我认为除了消除编译器警告之外,没有其他原因,而且在编译时有点可以强制执行类型约束。运行时不存储任何类型信息。正如您从像这样的问题中看到的那样,Java的泛型系统完全失效。 - Andrew Mao

2
如果在您的监听器中类型 T 并不重要,那么至少应该将方法定义为 void beforeAction(final MyEvent<?> event);,以消除警告并保持泛型启用。如果没有任何类型,则编译器将禁用该方法的所有类型检查。
如果您想要针对不同类型的事件拥有不同的监听器,则也应该像 Andrew 已经指出的那样将 T 添加到接口中。
这样一来,您就可以创建多个监听器实现,而无需进行手动转换(从而避免错误),例如:
public class StringListener implements MyListener<String> {
   void beforeAction(final MyEvent<String> event) {
     ...
   }
}

public class NumberListener implements MyListener<Number> {
   void beforeAction(final MyEvent<Number> event) {
     ...
   }
}

如果您有这样的实现,您也可以在运行时查询T的值,因为该类型信息存储在反射数据中。
请注意,对于匿名类或局部变量,情况并非如此 - 在这些情况下,类型擦除会发生。

你会建议我在监听器中使用 T 还是 ? 吗? - LuckyLuke
@LuckyLuke,这实际上取决于您想要使用该接口做什么。在大多数情况下,我会倾向于将T添加到接口中,并在实现中定义它,如上所示。 - Thomas

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接