我在编写代码时遇到了一些让我烦恼的事情。我在下面的代码示例中收集了两个示例。
cls1
行使用lambda表达式但不编译,而cls2
行使用方法引用和编译。我知道,如果我使用非通用对象,那么我就没有问题,但在这里,我使用泛型,更具体地说,使用通配符。
import java.lang.annotation.*;
import java.util.Optional;
public class MCVE {
static class Foo {}
static class Bar extends Foo {}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
static @interface Baz { Class<? extends Foo> value(); }
@Baz(Bar.class)
static class Quz {}
// Lambda expression - doesn't compile
Class<? extends Foo> cls1 = Optional.ofNullable(Quz.class.getAnnotation(Baz.class))
.map(baz -> baz.value())
.orElse(Bar.class);
// Method reference - compiles
Class<? extends Foo> cls2 = Optional.ofNullable(Quz.class.getAnnotation(Baz.class))
.map(Baz::value)
.orElse(Bar.class);
}
在功能方面,两条线都在做同样的事情。所以,我只是不明白在一个方法参考没有问题的情况下,使用lambda表达式失败的问题。
对于那些要问的人,编译时收到的错误如下:
MCVE.java:25: error: incompatible types: Class<Bar> cannot be converted to Class<CAP#1>
.orElse(Bar.class);
^
where CAP#1 is a fresh type-variable:
CAP#1 extends Foo from capture of ? extends Foo
Note: Some messages have been simplified; recompile with -Xdiags:verbose to get full output
1 error
这是相当简洁的,并没有提供任何非常有用的信息。
此外,请注意我已完成我的研究,这是错误与&#34;类似问题&#34;你可以在右侧面板上看到。这里有趣的部分是&#34;新型变量&#34;。
答案 0 :(得分:1)
想象一下,你有3个班级:
static class Foo {}
static class Bar extends Foo {}
static class Dem extends Foo {}
编译器将从lambda表达式中找到:
var x1 = Optional.ofNullable(Quz.class.getAnnotation(Baz.class));
// typeof x1 = Optional<Baz>
var x2 = x1.map(baz -> baz.value())
// typeof x2 = Optional<Class<T>>, where <T extends Foo> - this is the black magic you suffer with
// E.g. it can be
// a) Optional<Class<T=Foo>> or
// b) Optional<Class<T=Bar>> or
// c) Optional<Class<T=Dem>>
var x3 = x2.orElse(Bar.class);
// In case (a) and (b) - this code should work, in case (c) it should fail.
// Without additional explicit hint (conversion) compiler reports about this issue.
当你使用方法引用时 - 编译器会忽略所描述的类型推断并使用原始的Baz类型声明,所以
.map(Baz::value) // is identical to
.map(baz -> (Class<? extends Foo>) baz.value())