gpt4 book ai didi

java - 为什么 StreamEx 在收集到列表时强制我将 "? extends"添加到变量类型?

转载 作者:行者123 更新时间:2023-12-04 01:13:59 25 4
gpt4 key购买 nike

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

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 和 Java 11

最佳答案

这不是 StreamEx问题。当您使用 collect(Collectors.toList())StreamEx 上, 它同样有效。问题与 toList() 有关方便的方法,标准Stream甚至不提供。 StreamEx 是一个普遍的问题不应该受到责备。

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

public List<T> toList()

在一个完美的世界中,创建一个用父类(super class)型参数化的列表是合法的,即

public <R super T> List<R> toList()

但是在创建 Java 的泛型时,这个语法已经被省略了。 toArray两者的方法,CollectionStream遭受类似的限制;它们不能将结果数组的元素类型声明为集合元素类型的父类(super class)型。但是由于检查了实际数组的存储操作,这些方法只允许任何元素类型。对于 List结果,受类型删除影响,这是不可能的。

使用 collect(Collectors.toList()) 时另一方面,由于 collect,可以创建具有父类(super class)型的列表的签名:

<R,​A> R collect​(Collector<? super T,​A,​R> collector)

允许传入 Collector使用父类(super class)型 ( ? super T ) 参数化,在大多数情况下将从目标类型推断出。


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

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

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> 的父类(super class)型, 所以它适用于 collect如上所述,它允许收集到使用该父类(super class)型参数化的列表,但不能使用 toList .

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

private static List<Class<?>> fooList_StreamEx_Solved(List<Integer> input) {
return StreamEx.of(input)
.<Class<?>>map(x -> foo())
.toList();
}

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

关于java - 为什么 StreamEx 在收集到列表时强制我将 "? extends"添加到变量类型?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/63946023/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com