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

时间:2019-05-25 11:19:00

标签: 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;
    }

}

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

GenericTest.java:6: error: method orElse in class Optional cannot be applied to given types;
                Optional.empty().map(o -> getStringClass(o)).orElse(String.class);
                                                            ^
  required: Class<CAP#1>
  found: Class<String>
  reason: argument mismatch; Class<String> cannot be converted to Class<CAP#1>
  where T is a type-variable:
    T extends Object declared in class Optional
  where CAP#1 is a fresh type-variable:
    CAP#1 extends Object from capture of ?
1 error

但是,如果我改用方法引用,则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<Object,Class<?>>,因此在这里看不到区别。
Eclipse Java编译器(ecj)不会同时编译两个版本。

2 个答案:

答案 0 :(得分:4)

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

可能的解决方法?使用显式键入的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,存在类似问题。

答案 1 :(得分:3)

方法链接再次出现。您可以阅读为什么here,该功能的设计人员发现实现起来很复杂(这与lambda的/方法引用是 poly表达式有关。 -它们的类型取决于上下文)。这样的功能将给编译器带来一些额外的负担-尽管您的示例将是一个相当琐碎的示例,但是javac所关心的不仅仅是琐碎的事情;它还涉及很多其他方面。因此,即使在java-12中,也尚未实现。

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

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