首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >为什么StreamEx强迫我在收集到列表时将“扩展”添加到变量类型?

为什么StreamEx强迫我在收集到列表时将“扩展”添加到变量类型?
EN

Stack Overflow用户
提问于 2020-09-17 20:50:26
回答 1查看 145关注 0票数 3

我注意到,在低于标准Java流的情况下,“计算”变量类型比StreamEx更好。这很奇怪,因为人们喜欢StreamEx,在任何地方都使用它,但是代码却被"?“污染了。我想使用List<Class<?>,但StreamEx强迫我使用List<? extends Class<?>>。有人能解释一下为什么StreamEx是这样工作的吗?我可以在StreamEx中使用所需的变量类型吗?

代码语言:javascript
复制
private static List<? extends Class<?>> 
fooList_StreamEx_Compiles(List<Integer> input) {
    return StreamEx.of(input)
            .map(x -> foo())
            .toList();
}

private static List<Class<?>> 
fooList_StreamEx_Error(List<Integer> input) {
    return StreamEx.of(input)
            .map(x -> foo())
// Error: incompatible types: java.util.List<java.lang.Class<capture#1 of ?>> 
// cannot be converted to java.util.List<java.lang.Class<?>>
            .toList();
}

private static List<Class<?>> fooList(List<Integer> input) {
    return input
            .stream()
            .map(x -> foo())
            .collect(Collectors.toList());
}

private static Class<?> foo() {
    return String.class;
}

我使用的是StreamEx 0.7.0和Java11

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2020-09-18 12:48:26

这不是StreamEx问题。当您在collect(Collectors.toList())上使用StreamEx时,它同样工作得很好。这个问题与toList()便利方法有关,标准Stream甚至没有提供这种方法。这是一个不能归咎于StreamEx的普遍问题。

toList方法在StreamEx上具有以下签名:

代码语言:javascript
复制
public List<T> toList()

在完美的世界中,用超级类型创建参数化的列表是合法的,例如

代码语言:javascript
复制
public <R super T> List<R> toList()

但是,在创建Java的泛型时,忽略了这个语法。toArray方法、Collection方法和Stream方法都有类似的限制;它们不能将结果数组的元素类型声明为集合元素类型的超级类型。但是,由于检查了实际数组的存储操作,这些方法只允许任何元素类型。对于List结果,在类型擦除的前提下,这是不可能的。

另一方面,当使用collect(Collectors.toList())时,由于collect的签名,可以创建一个超级类型的列表:

代码语言:javascript
复制
<R,​A> R collect​(Collector<? super T,​A,​R> collector)

它允许传递具有超级类型( Collector )的参数化的? super T,在大多数情况下,超级类型将从目标类型推断。

toList声明的此限制与另一个限制交互,即不合理地处理通配符类型。我不确定导致这个问题的原因是编译器还是规范,但是在map(x -> foo())步骤中,通配符类型被捕获,并且这种捕获类型将被认为不同于任何其他捕获的通配符类型,即使它来自同一个源。

当我用javac编译您的代码时,它说:

代码语言:javascript
复制
error: incompatible types: List<Class<CAP#1>> cannot be converted to List<Class<?>>
                    .toList();
                           ^
  where CAP#1 is a fresh type-variable:
    CAP#1 extends Object from capture of ?
1 error

CAP#1是捕获的类型。所有捕获的类型都会被编号,以区分它们。如前所述,它们中的每一种都被认为是一种不同的类型,与每一种不同。

Class<?>Class<CAP#1>的超级类型,因此它与collect一起工作,它允许收集参数化的列表,如上面所述,而不是toList

您可以通过使用返回类型List<? extends Class<?>>来修复这个问题,以表示列表的实际元素类型是Class<?>的一个子类型,但更好的选择是强制编译器不要使用捕获的类型:

代码语言:javascript
复制
private static List<Class<?>> fooList_StreamEx_Solved(List<Integer> input) {
    return StreamEx.of(input)
            .<Class<?>>map(x -> foo())
            .toList();
}

如果您不完全理解在这里插入显式类型的必要性,请不要担心,我并不是说在涉及…的通配符类型时,Java编译器的行为是可理解的。

票数 4
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/63946023

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档