当我尝试编译此代码时
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)不会同时编译两个版本。
答案 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);