【问题标题】:filtering a stream changes its wildcard bounds?过滤流会改变其通配符范围?
【发布时间】:2022-01-22 18:15:55
【问题描述】:

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

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.stream.Stream>

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

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

【问题讨论】:

  • > 似乎可以解决问题,但我想知道为什么

标签: java generics java-stream unbounded-wildcard


【解决方案1】:

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

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

Stream.filter() 不是:

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

因此,在第一种情况下,编译器在推断 numbers.map(...) 的类型时可以考虑返回语句的上下文(getNumbers 的类型)。
在第二种情况下,编译器不能对numbers.map(...) 做同样的事情,因为有后续的链接调用,这可能会进一步改变类型,所以在这个阶段很难猜测正确的推断应该是什么。因此,最具体的可能类型被假定为numbers.map(...) (Stream&lt;Optional&lt;Number&gt;&gt;),并由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() 在那个case 被默认为Object(是的,当你有return List.of(); 时,返回类型推断开始,迫使编译器找出意图是使E 匹配String,方法返回中的类型参数类型)。 请注意,这比这更复杂,有些角落的推理无法按预期/预期工作。


简答return numbers.map(Optional::of) 利用类型推断,因为 map() 是通用的,而 filter() 没有,期望携带 Stream&lt;E&gt;E。对于numbers.map(Optional::of)EOptional&lt;Number&gt;,而不是Optional&lt;? extends Number&gt;,而filter 就是这样。

【讨论】:

  • 我仍然不明白是什么阻止编译器在第二种情况下首先对numbers.map(...) 部分进行类型分析,按照您的解释利用类型推断,然后然后filter(...) 应用到它...
  • 我的意思是,Stream&lt;Optional&lt;? extends Number&gt;&gt; t = numbers.map(Optional::of); return t.filter(number -&gt; true); 也可以正常工作,感觉真的很不一致,但是省略 t 分配会破坏它......
  • 啊,我想我明白了:在部分类型分析的情况下,它缺乏推断它的上下文(以后可能会有其他映射来回改变类型,所以它会是编译器很难在这个阶段猜测正确的推理应该是什么)。
  • @morgwai 现场。当之后有链式方法时,编译器不携带推断类型。好吧,在这种情况下,它几乎不会进行类型推断。
猜你喜欢
  • 2013-12-24
  • 2011-04-03
  • 1970-01-01
  • 2011-10-10
  • 1970-01-01
  • 2011-10-21
  • 2015-03-03
  • 2013-01-24
  • 1970-01-01
相关资源
最近更新 更多