如何多次实现相同的界面,但具有不同的泛型?

时间:2014-03-03 04:04:35

标签: java generics interface

我有以下界面,我想在我的课程中多次实现:

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>

是否可以使用不同的泛型实现两次接口?如果没有,那么我能做到的最接近的事情是什么才能实现我在这里要做的事情?

3 个答案:

答案 0 :(得分:18)

  

是否可以使用不同的泛型实现两次接口

不幸的是没有。您无法两次实现相同接口的原因是因为类型擦除。编译器将处理类型参数,而运行时EventListener<X>只是EventListener


  

如果没有,那么我能做到的最接近的事情是如何才能实现我在这里所做的一切?

类型擦除对我们有利。一旦您知道EventListener<X>EventListener<Y>在运行时只是原始EventListener,那么编写一个可以处理不同类型EventListener的{​​{1}}比您想象的要容易{1}}。 Bellow是一个通过Events IS-A测试的解决方案,通过简单授权正确处理EventListenerLogin事件:

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      
    }   
}