过滤流会更改其通配符范围吗?

mor*_*wai 8 java generics unbounded-wildcard java-stream

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

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

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

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

它会生成以下错误:

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

在 openJdk-11 和 openJdk-17 上进行了测试。

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

ern*_*t_k 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);
Run Code Online (Sandbox Code Playgroud)

Stream.filter()不是:

Stream<T> filter(Predicate<? super T> predicate);
Run Code Online (Sandbox Code Playgroud)

因此,在第一种情况下,编译器在推断 的类型时可以考虑 return 语句的上下文(getNumbers's 类型)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();
}
Run Code Online (Sandbox Code Playgroud)

现在,为什么会失败:

static List<String> stringList() {
    return List.of().subList(0, 0);
}
Run Code Online (Sandbox Code Playgroud)

这是因为List.subList(...)不会E在上下文中推断返回列表的类型(即,该方法不是通用的),它携带List实例的E类型,List.of()在这种情况下,该类型默认为Object(是的,当你有 时return List.of();,返回类型推断开始,强制编译器弄清楚其意图是使Ematch String(方法的返回类型中的类型参数)。请注意,这会变得更加复杂,有些地方推理无法按希望/预期进行。


简短的回答return numbers.map(Optional::of)利用类型推断作为map()通用类型,而filter()不是期望携带Eof 。Stream<E>numbers.map(Optional::of),EOptional<Number>, 不是Optional<? extends Number>, 并filter带有该。

  • 啊,我想我明白了:在部分类型分析的情况下,它缺乏推断它的上下文(稍后可能有其他映射来回更改类型,因此编译器很难猜测它是什么)正确的推断应该是在这个阶段)。 (3认同)
  • @morgwai 准确。当后面有链式方法时,编译器不会携带推断类型。嗯,在这种情况下它几乎不进行类型推断。 (2认同)