【问题标题】:Why can't Java infer the type of a lambda expression with a generic type parameter?为什么 Java 不能用泛型类型参数推断 lambda 表达式的类型?
【发布时间】:2019-06-20 07:37:36
【问题描述】:

给定以下代码:

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 表达式传递给 subscribe 方法来使用 SubscriberManager

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

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

不兼容的类型:EventSubscriber 不是函数式接口 在接口 EventSubscriber 中找到多个非覆盖抽象方法

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

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

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

  }
});

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

class SubscriberManager {

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

  public void subscribe(MyEventSubscriber subscriber) {
  }
}

【问题讨论】:

  • 错误信息 - incompatible types: EventSubscriber is not a functional interface multiple non-overriding abstract methods found in interface EventSubscriber - 不够清楚吗?您的接口不是函数式接口,因此没有 lambda 表达式可以工作。它与泛型无关。
  • 在第二个代码示例中,您可以看到我成功地将 lambda 表达式传递给 subscribe 方法,但我需要明确指定 lambda 表达式的类型。
  • 嗯,MyEventSubscriber 是一个函数式接口,所以你可以使用 lambda 表达式来实现它。 EventSubscriber 不是。
  • 我想我的问题是 - 为什么 Java 不能从我的示例中为 MyEvent 的 Lamba 参数推断出 lambda 表达式的类型?
  • subscriberManager.subscribe 需要一个EventSubscriber,它不是一个功能接口。因此,您不能将 lambda 表达式直接传递给它(除非您将 lambda 表达式显式转换为 MyEventSubscriber)。

标签: java generics lambda


【解决方案1】:

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

Java 的业务不是搜索类路径中的数百万个类,只是为了查看整个混乱中是否有任何可能或可能不起作用的东西。我希望这样说可以清楚地说明为什么会这样以及为什么它永远不会那样工作。

具体来说:因为有一个 lambda,java 需要将它作为目标类型。为此,java 首先检查 lambda 周围的上下文:它检查名为 subscribe 的各种方法,并注意到只有一个方法,它需要一个类型为 EventSubscriber 的参数。然后它将 lambda 目标类型化为这种类型,并立即失败,因为它不是函数式接口。编译器无法确定它应该是目标类型MyEventSubscriber

我玩弄了一些使用反射来检查实际类型的方法,但这不起作用;你必须找到另一种方法来解决这个问题。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-02-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-01-15
    相关资源
    最近更新 更多