【问题标题】:Why does it matter if I use a method reference or a lambda here?为什么我在这里使用方法引用或 lambda 很重要?
【发布时间】:2019-05-25 11:18:59
【问题描述】:

当我尝试编译这段代码时

import java.util.Optional;

public class GenericTest {

    public static void main(String[] args) {
        Optional.empty().map(o -> getStringClass(o)).orElse(String.class);
    }

    static Class<?> getStringClass(Object arg) {
        return String.class;
    }

}

javac 将失败并出现以下错误:

GenericTest.java:6: 错误:类 Optional 中的方法 orElse 不能应用于给定类型;
                Optional.empty().map(o -> getStringClass(o)).orElse(String.class);
                                                            ^
  必需:类
  找到:类
  原因:参数不匹配; Class 无法转换为 Class
  其中 T 是一个类型变量:
    T 扩展类 Optional 中声明的 Object
  其中 CAP#1 是一个新的类型变量:
    CAP#1 从捕获的 ?
1 个错误

但是如果我改用方法引用,javac 将编译代码就好了:

import java.util.Optional;

public class GenericTest {

    public static void main(String[] args) {
        Optional.empty().map(GenericTest::getStringClass).orElse(String.class);
    }

    static Class<?> getStringClass(Object arg) {
        return String.class;
    }

}

为什么我使用方法引用或 lambda 表达式会有所不同?

根据我的理解,方法引用和 lambda 的类型都是Function&lt;Object,Class&lt;?&gt;&gt;,所以我看不出这里有什么区别。
顺便说一句,eclipse java编译器(ecj)不会同时编译这两个版本。

【问题讨论】:

  • 您使用的是哪个版本的 javac?
  • 请提供版本,这两种方式都无法为我编译。 Java-8
  • javac 1.8.0_172 和 javac 10.0.2。我知道 Java 10 不是最新的,但我已经安装了它,所以..
  • 您使用哪种 Java 实现?你从哪里得到的,甲骨文?
  • java 版本 "1.8.0_172" Java(TM) SE Runtime Environment (build 1.8.0_172-b11) Java HotSpot(TM) 64-Bit Server VM (build 25.172-b11, mixed mode)

标签: java lambda javac method-reference


【解决方案1】:

方法链再次出现。您可以阅读here为什么,该功能的设计者发现实现起来很复杂(这与 lambda 的/方法引用是 poly 表达式这一事实有关 - 他们的类型取决于上下文)。这样的功能需要给编译器带来一些额外的负担 - 虽然您的示例将是一个相当简单的示例来解决,但javac 必须关心的不仅仅是琐碎;因此这还没有实现,即使在 java-12 中也是如此。

IMO 最简单的解决方案,因为这与方法链接有关(在您的情况下这是可能的),而不是链接:

Optional<Class<?>> first = Optional.empty().map(o -> getStringClass(o));
Class<?> second = first.orElse(String.class);

【讨论】:

  • 是的,不链接是一个合适的解决方案,但我在问为什么我可以编译第二段代码。
  • @JohannesKuhn 你不能...要么是一些奇怪的 javac 版本,要么你使用 eclipse ecj 和旧版本
  • @JohannesKuhn 对。我根本不在乎 IDEA 显示了什么,只关心 javac 做了什么,它们确实编译得很好,这不是我所期望的。
  • 是的。是的,它编译得很好。这是出乎意料的。这就是我的问题,因为我不够聪明,无法自己回答这个问题。我不在乎如何避免这个错误,只是“有人可以解释这个(对我来说意外的)行为”。
  • @JohannesKuhn this 看起来像解释......我会尽快删除这个答案,因为你是正确的
【解决方案2】:

这是编译器类型推断系统的已知limitation,它不适用于您的第一个代码 sn-p 中的链式方法调用。

可能的解决方法?使用显式类型的 lambda 表达式:

Optional.empty()
        .map((Function<Object, Class<?>>) o -> getStringClass(o))
        .orElse(String.class);

或确切的方法参考(您已经尝试过):

Optional.empty().map(GenericTest::getStringClass).orElse(String.class);

或添加类型见证:

Optional.empty().<Class<?>>map(o -> getStringClass(o)).orElse(String.class);

Related post 有类似问题。

【讨论】:

  • 您知道为什么第一段代码不起作用,而第二段代码起作用吗?我知道如何避免这种错误,这不是问题。请再次阅读该问题,如果您知道为什么它的行为不同,请随时发表解释。
猜你喜欢
  • 1970-01-01
  • 2012-02-26
  • 1970-01-01
  • 2013-03-19
  • 2019-02-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多