我有以下界面,我想在我的课程中多次实现:
public interface EventListener<T extends Event>
{
public void onEvent(T event);
}
现在,我希望能够以下列方式实现此接口:
class Foo implements EventListener<LoginEvent>, EventListener<LogoutEvent>
{
@Override
public void onEvent(LoginEvent event)
{
}
@Override
public void onEvent(LogoutEvent event)
{
}
}
但是,这会在错误上显示错误:Duplicate class com.foo.EventListener
:
class Foo implements EventListener<LoginEvent>, EventListener<LogoutEvent>
是否可以使用不同的泛型实现两次接口?如果没有,那么我能做到的最接近的事情是什么才能实现我在这里要做的事情?
答案 0 :(得分:18)
是否可以使用不同的泛型实现两次接口
不幸的是没有。您无法两次实现相同接口的原因是因为类型擦除。编译器将处理类型参数,而运行时EventListener<X>
只是EventListener
如果没有,那么我能做到的最接近的事情是如何才能实现我在这里所做的一切?
类型擦除对我们有利。一旦您知道EventListener<X>
和EventListener<Y>
在运行时只是原始EventListener
,那么编写一个可以处理不同类型EventListener
的{{1}}比您想象的要容易{1}}。 Bellow是一个通过Events
IS-A
测试的解决方案,通过简单授权正确处理EventListener
和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
来完成它,但是还有很多其他可能的实现。你可以使用像@ user949300建议的匿名内部类,你可以在Event类中包含一个class
鉴别器,以了解每个事件的作用等等。
通过将此代码用于所有效果,您将创建一个能够处理两种事件的getEventType
。解决方法是100%自包含(无需公开内部EventListener
)。
最后,最后一个问题可能会打扰你。在编译时EventListeners
类型实际上是Foo
。现在,您无法控制的API方法可能需要参数化EventListener
s:
EventListener
同样,在运行时,这两种方法都处理原始public void addLoginListener(EventListener<LoginEvent> event) { // ...
// OR
public void addLogoutListener(EventListener<LogoutEvent> event) { // ...
。因此,通过让EventListener
实现原始接口,编译器很乐意让您远离类型安全警告(您可以忽略Foo
):
@SuppressWarnings("unchecked")
虽然所有这些看起来令人生畏,但只要重复一遍“编译器试图欺骗我(或拯救我);没有 spoon eventSource.addLoginListener(foo); // works
。一旦你划伤你的在Java 1.5使用充满类型参数的现代代码之前尝试编写遗留代码几个月,类型擦除成为你的第二天性。
答案 1 :(得分:8)
您需要使用内部或匿名类。例如:
class Foo {
public EventListener<X> asXListener() {
return new EventListener<X>() {
// code here can refer to Foo
};
}
public EventListener<Y> asYListener() {
return new EventListener<Y>() {
// code here can refer to Foo
};
}
}
答案 2 :(得分:0)
这是不可能的。 但为此你必须创建两个不同的类,用两个不同的参数实现EventListener接口。
public class Login implements EventListener<LoginEvent> {
public void onEvent(LoginEvent event) {
// TODO Auto-generated method stub
}
}
public class Logout implements EventListener<LogoutEvent> {
public void onEvent(LogoutEvent event) {
// TODO Auto-generated method stub
}
}