8

下面的方法编译没有问题:

static Stream<Optional<? extends Number>> getNumbers(Stream<Number> numbers) {
    return numbers.map(Optional::of);
}

但是,如果我像这样添加一个简单的过滤器:

static Stream<Optional<? extends Number>> getNumbers2(Stream<Number> numbers) {
    return numbers.map(Optional::of).filter(number -> true);
}

它会产生以下错误:

不兼容的类型:
java.util.stream.Stream<java.util.Optional<java.lang.Number>> 无法转换为
java.util.stream.Stream<java.util.Optional<? 扩展 java.lang.Number>>

在 openJdk-11 和 openJdk-17 上测试。

我希望他们都做同样的事情(要么都编译好,要么都产生相同的编译错误),所以我对此感到非常困惑:这里的一般规则是什么,解释了为什么第一种方法可以编译但第二种方法才不是?谢谢!

4

1 回答 1

8

与第一种情况下的返回类型的兼容性Stream<Optional<? extends Number>>不是靠自己numbers.map(Optional::of)返回a来获得的;Stream<Optional<? extends Number>>它是编译器推断的返回类型,numbers.map(...)因为它是一个通用方法:

<R> Stream<R> map(Function<? super T, ? extends R> mapper);

Stream.filter()不是:

Stream<T> filter(Predicate<? super T> predicate);

因此,在第一种情况下,编译器在推断类型时可以考虑返回语句的上下文(getNumbers的类型)numbers.map(...)
编译器不能numbers.map(...)在第二种情况下做同样的事情,因为有后续的链接调用,这可能会进一步改变类型,所以在这个阶段很难猜测正确的推断应该是什么。结果,为numbers.map(...)( Stream<Optional<Number>>) 假定了最具体的可能类型,并由 进一步进行filter(...)

作为一个不同的例子来说明这一点,请弄清楚为什么这两个编译(List.of()毕竟是相同的代码):

static List<String> stringList() {
    return List.of();
}
static List<Integer> intList() {
    return List.of();
}

现在,为什么会失败:

static List<String> stringList() {
    return List.of().subList(0, 0);
}

这是因为List.subList(...)它不会E在上下文中推断返回列表的类型(即,该方法不是通用的),它带有List实例的E类型,List.of()在这种情况下默认为Object(是的,当你有时return List.of();,返回类型推断开始,强制编译器确定其意图是E匹配String方法的返回类型中的类型参数)。请注意,这比这更复杂,有些角落的推理无法按预期/预期工作。


简短的回答return numbers.map(Optional::of)利用类型推断map()是通用的,而filter()不是期望EofStream<E>被携带。并带有numbers.map(Optional::of), Eis Optional<Number>, not Optional<? extends Number>, 并filter带有那个。

于 2021-12-21T10:22:42.457 回答