【发布时间】:2016-06-09 21:55:47
【问题描述】:
我在编写一些代码时遇到了一些让我烦恼的事情。我在下面的代码示例中收集了两个示例。
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
这是相当简洁的,并没有提供任何非常有用的信息。
另外,请注意,我已经完成了研究,这个错误与您在右侧面板上看到的“类似问题”中的错误不同。这里有趣的部分是“新鲜类型变量”。
【问题讨论】:
-
总的来说:Java 的类型系统如今几乎是一个黑盒子,关于何时显式类型是必要和不必要的一般规则的方式并不多。
-
@Hosseini 谢谢你的链接。虽然它比我在“类似问题”中看到的更接近我所经历的,但它谈论的是分配——我想这在这里无关紧要——它没有解决为什么 lambda 不行的问题,但可以使用方法参考。
-
在 Eclipse 中,我在这两种情况下都会出错。将
map的结果分配给Optional<Class<? extends Foo>>类型的中间变量可以解决这两种情况。推断orElse的参数显示Class<? extends Foo>所以Bar.class应该是适用的。奇怪... -
@JornVernee 我也这样做了;但是,如果您手动编译,则 cls2 行不会出错。可能是 Eclipse 中的一个错误。