Java为什么不能使用泛型类型参数来推断lambda表达式的类型?

时间:2019-06-20 07:37:36

标签: java generics lambda

给出以下代码:

abstract class Event {
}

class MyEvent extends Event {
}

interface EventSubscriber<T extends Event> {

  void onMessage(T message);

  Class<T> getMessageType();
}

interface MyEventSubscriber extends EventSubscriber<MyEvent> {

  @Override
  default Class<MyEvent> getMessageType() {
    return MyEvent.class;
  }
}

class SubscriberManager {

  public void subscribe(EventSubscriber<? extends Event> subscriber) {
  }
}

我想通过调用getMessageType方法来访问事件订阅者持有的通用类型参数。 我还想通过将lambda表达式传递给SubscriberManager方法来使用subscribe

subscriberManager.subscribe((MyEvent event) -> {});

不幸的是,Java编译器无法推断传递给subscribe方法的lambda表达式的类型,尽管对我来说很明显,可以从lambda的参数推导出lambda的类型- MyEvent-> MyEventSubscriber。 Java编译器给我以下错误:

  

不兼容的类型:EventSubscriber不是功能接口       接口EventSubscriber中找到多个非重写的抽象方法

所以我需要指定lambda表达式的类型或使用匿名类来绕过此限制:

MyEventSubscriber myEventSubscriber = (MyEvent event) -> {};
subscriberManager.subscribe(myEventSubscriber);

subscriberManager.subscribe(new MyEventSubscriber() {
  @Override
  public void onMessage(MyEvent message) {

  }
});

我可以向SubscriberManager类中添加一个重载方法,从getMessageType接口中删除EventSubscriber方法(因为我们将知道订户的实际类型,因此也知道消息类型)并使用我在第一个代码示例中提到的简单的lambda表达式,但是我想它会使整个代码变得不那么“多态”:

class SubscriberManager {

  public void subscribe(EventSubscriber<? extends Event> subscriber) {
  }

  public void subscribe(MyEventSubscriber subscriber) {
  }
}

1 个答案:

答案 0 :(得分:0)

问题是您的EventSubscriber接口不是一个功能接口,因为该错误告诉您:有2种方法可以在那里实现。您已经创建了一个名为MyEventSubscriber的实际功能接口,这意味着您希望Java以某种方式直觉它的存在。

Java并不是要在类路径中搜寻数百万个类,只是为了看一看整个混乱中是否有任何东西可能有效或无效。我希望以这种方式说清楚,这是显而易见的,为什么它永远也不会那样工作。

特别是:因为有一个lambda,所以java需要以目标输入它。为此,java首先检查lambda周围的上下文:它检查名为subscribe的各种方法,并注意到只有一个,并且它需要一个EventSubscriber类型的参数。然后,它将lambda定位为该类型,并立即失败,因为它不是功能接口。编译器无法确定应该将目标类型设置为MyEventSubscriber

我在尝试使用反射来检查实际类型,但这是行不通的;您将不得不找到另一种解决此问题的方法。