为什么在这里使用方法引用或lambda为何如此重要?

Joh*_*uhn 6 java lambda javac method-reference

当我尝试编译这段代码时

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

}
Run Code Online (Sandbox Code Playgroud)

javac将失败,并显示以下错误:

GenericTest.java:6:错误:类别Optional中的方法orElse无法应用于给定类型;
                Optional.empty()。map(o-> getStringClass(o))。orElse(String.class);
                                                            ^
  必需:Class <CAP#1>
  找到:类<String>
  原因:参数不匹配;Class <String>无法转换为Class <CAP#1>
  其中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;
    }

}
Run Code Online (Sandbox Code Playgroud)

如果使用方法引用或lambda表达式,为什么会有区别?

根据我的理解,方法引用和lambda都具有type Function<Object,Class<?>>,所以在这里我看不到区别。
Eclipse Java编译器(ecj)不会同时编译两个版本。

Ole*_*hov 6

这是编译器类型推断系统的一个已知限制,它不适用于第一个代码片段中的链式方法调用。

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

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

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

Optional.empty().map(GenericTest::getStringClass).orElse(String.class);
Run Code Online (Sandbox Code Playgroud)

或添加类型见证:

Optional.empty().<Class<?>>map(o -> getStringClass(o)).orElse(String.class);
Run Code Online (Sandbox Code Playgroud)

具有类似问题的相关帖子。


Eug*_*ene 6

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

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

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