是否可以用不同的泛型实现接口两次
很遗憾,没有。您不能两次实现相同接口的原因是类型擦除。编译器将处理类型参数,运行时EventListener<X> 只是一个EventListener
如果不是,我能做的下一个最接近的事情是什么?
类型擦除对我们有利。一旦您知道EventListener<X> 和EventListener<Y> 在运行时只是原始EventListener,编写一个可以处理不同类型Events 的EventListener 比您想象的要容易。 Bellow 是一个通过 EventListener 的 IS-A 测试并通过简单委托正确处理 Login 和 Logout 事件的解决方案:
@SuppressWarnings("rawtypes")
public class Foo implements EventListener {
// Map delegation, but could be anything really
private final Map<Class<? extends Event>, EventListener> listeners;
// Concrete Listener for Login - could be anonymous
private class LoginListener implements EventListener<LoginEvent> {
public void onEvent(LoginEvent event) {
System.out.println("Login");
}
}
// Concrete Listener for Logout - could be anonymous
private class LogoutListener implements EventListener<LogoutEvent> {
public void onEvent(LogoutEvent event) {
System.out.println("Logout");
}
}
public Foo() {
@SuppressWarnings("rawtypes")
Map<Class<? extends Event>, EventListener> temp = new HashMap<>();
// LoginEvents will be routed to LoginListener
temp.put(LoginEvent.class, new LoginListener());
// LogoutEvents will be routed to LoginListener
temp.put(LogoutEvent.class, new LogoutListener());
listeners = Collections.unmodifiableMap(temp);
}
@SuppressWarnings("unchecked")
@Override
public void onEvent(Event event) {
// Maps make it easy to delegate, but again, this could be anything
if (listeners.containsKey(event.getClass())) {
listeners.get(event.getClass()).onEvent(event);
} else {
/* Screams if a unsupported event gets passed
* Comment this line if you want to ignore
* unsupported events
*/
throw new IllegalArgumentException("Event not supported");
}
}
public static void main(String[] args) {
Foo foo = new Foo();
System.out.println(foo instanceof EventListener); // true
foo.onEvent(new LoginEvent()); // Login
foo.onEvent(new LogoutEvent()); // Logout
}
}
存在抑制警告是因为我们“滥用”类型擦除并根据事件具体类型委托给两个不同的事件侦听器。我选择使用HashMap 和运行时事件class 来实现,但还有很多其他可能的实现。您可以使用建议的 @user949300 之类的匿名内部类,您可以在 Event 类中包含 getEventType 鉴别器以了解每个事件的处理方式等等。
通过对所有效果使用此代码,您将创建一个能够处理两种事件的EventListener。解决方法是 100% 自包含(无需公开内部 EventListeners)。
最后,还有一个问题可能会困扰您。在编译时Foo 类型实际上是EventListener。现在,您无法控制的 API 方法可能需要参数化 EventListeners:
public void addLoginListener(EventListener<LoginEvent> event) { // ...
// OR
public void addLogoutListener(EventListener<LogoutEvent> event) { // ...
同样,在运行时,这两种方法都处理原始的EventListeners。因此,通过让Foo 实现一个原始接口,编译器将很乐意让您摆脱一个类型安全警告(您可以使用@SuppressWarnings("unchecked") 忽略它):
eventSource.addLoginListener(foo); // works
虽然所有这些看起来令人生畏,但只要对自己重复一遍“编译器试图欺骗我(或拯救我);没有 spoon <T>。一旦你为几个月试图让 Java 1.5 之前编写的遗留代码与充满类型参数的现代代码一起工作,类型擦除成为你的第二天性。