过滤流会改变它的通配符边界吗?

filtering a stream changes its wildcard bounds?

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

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);
}

它生成以下错误:

incompatible types:
java.util.stream.Stream<java.util.Optional<java.lang.Number>> cannot be converted to
java.util.stream.Stream<java.util.Optional<? extends java.lang.Number>>

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

我希望他们都做同样的事情(要么都编译正常,要么都产生相同的编译错误),所以我真的很困惑:这里解释为什么第一种方法的一般规则是什么编译好但第二个没有? 谢谢!

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

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

Stream.filter() 不是:

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

因此,在第一种情况下,编译器在推断 numbers.map(...).
的类型时可以考虑 return 语句的上下文(getNumbers 的类型) 在第二种情况下,编译器不能对 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(...)不推断returned列表的E上下文类型(即方法不是通用的),它携带List实例的E 类型,在这种情况下,List.of() 默认为 Object(是的,当你有 return List.of();、return 类型推断时,强制编译器找出其目的是使 E 匹配 String,方法的 return 类型中的类型参数)。 请注意,这比那更复杂,有些地方推理不起作用,因为 wished/expected。


简答return numbers.map(Optional::of)利用了类型推断,因为map()是通用的,而filter()不是,期望E of Stream<E> 被携带。对于 numbers.map(Optional::of)EOptional<Number>,而不是 Optional<? extends Number>,而 filter