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

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&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 现场。当之后有链式方法时,编译器不携带推断类型。好吧,在这种情况下,它几乎不会进行类型推断。

以上是关于过滤流会改变其通配符范围?的主要内容,如果未能解决你的问题,请参考以下文章

mysql必知必会--用通配符进行过滤

Laravel,路由通配符过滤然后控制器

tasklist.exe 中的过滤器不使用通配符?

tasklist.exe中的过滤器不带通配符?

通配符掩码

通配符和正则表达式-grep,egrep,fgrep