过滤流会改变其通配符范围?
Posted
技术标签:
【中文标题】过滤流会改变其通配符范围?【英文标题】: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 上测试。
我希望它们都做同样的事情(要么都编译正常,要么都产生相同的编译错误),所以我对此感到非常困惑:这里的一般规则是什么,解释了为什么第一种方法编译正常然而第二个没有? 谢谢!
【问题讨论】:
流> 似乎可以解决问题,但我想知道为什么 【参考方案1】:与第一种情况下的返回类型Stream<Optional<? extends Number>>
的兼容性不是靠numbers.map(Optional::of)
自己返回一个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);
因此,在第一种情况下,编译器在推断 numbers.map(...)
的类型时可以考虑返回语句的上下文(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(...)
没有在上下文中推断返回列表的E
类型(即,该方法不是通用的),它携带List
实例的E
类型,其中List.of()
在那个case 被默认为Object
(是的,当你有return List.of();
时,返回类型推断开始,迫使编译器找出意图是使E
匹配String
,方法返回中的类型参数类型)。 请注意,这比这更复杂,有些角落的推理无法按预期/预期工作。
简答:return numbers.map(Optional::of)
利用类型推断,因为 map()
是通用的,而 filter()
没有,期望携带 Stream<E>
的 E
。对于numbers.map(Optional::of)
,E
是Optional<Number>
,而不是Optional<? extends Number>
,而filter
就是这样。
【讨论】:
我仍然不明白是什么阻止编译器在第二种情况下首先对numbers.map(...)
部分进行类型分析,按照您的解释利用类型推断,然后然后 将filter(...)
应用到它...
我的意思是,Stream<Optional<? extends Number>> t = numbers.map(Optional::of); return t.filter(number -> true);
也可以正常工作,感觉真的很不一致,但是省略 t
分配会破坏它......
啊,我想我明白了:在部分类型分析的情况下,它缺乏推断它的上下文(以后可能会有其他映射来回改变类型,所以它会是编译器很难在这个阶段猜测正确的推理应该是什么)。
@morgwai 现场。当之后有链式方法时,编译器不携带推断类型。好吧,在这种情况下,它几乎不会进行类型推断。以上是关于过滤流会改变其通配符范围?的主要内容,如果未能解决你的问题,请参考以下文章