为啥新的 Java 8 流在 toArray 调用上返回一个对象数组?
Posted
技术标签:
【中文标题】为啥新的 Java 8 流在 toArray 调用上返回一个对象数组?【英文标题】:Why do the new Java 8 streams return an Object Array on toArray calls?为什么新的 Java 8 流在 toArray 调用上返回一个对象数组? 【发布时间】:2014-12-27 02:35:43 【问题描述】:当我从事一个涉及 Java 8 新流的项目时,我注意到当我在流上调用 Stream#toArray()
时,它返回一个 Object[]
而不是 T[]
。尽管我很惊讶,但我开始深入研究 Java 8 的源代码,但找不到任何原因他们没有将 Object[] toArray();
实现为 T[] toArray();
。这背后有什么原因吗,还是只是(不)一致性?
编辑 1: 我注意到很多人在回答中说这是不可能的,但是这段代码sn-p编译并返回了预期的结果?
import java.util.Arrays;
public class Test<R>
private Object[] items;
public Test(R[] items)
this.items = items;
public R[] toArray()
return (R[]) items;
public static void main(String[] args)
Test<Integer> integerTest = new Test<>(new Integer[]
1, 2, 3, 4
);
System.out.println(Arrays.toString(integerTest.toArray()));
【问题讨论】:
它怎么知道R
?如果您希望返回特定类型,请将生成器传递给该方法。例如对于字符串数组,请执行以下操作:streamString.toArray(String[]::new)
您不知道应该转换为哪种类型,因为在运行时 Java 不知道实际类型,因为 类型擦除(就像它在@Susei 的回答)
Object[]
不一定是R[]
。该演员在运行时肯定会失败。
对象没有扩展R
,所以这会抛出一个RuntimeException
R[]
的演员表应该是你的警告信号。这是一个 unchecked 强制转换,编译器会告诉你(但你可能只是忽略了这一点)。 items[]
不是 R[]
的数组,所以编译器说它不能验证这个转换是完全正确的——它不能阻止客户端转换返回的数组到Object[]
,然后将非 R 值放入其中,这将颠覆假设的类型安全性,即“这是一个 R 数组”。
【参考方案1】:
这与List#toArray()
的问题相同。类型擦除使我们无法知道应该返回的数组类型。考虑以下
class Custom<T>
private T t;
public Custom (T t) this.t = t;
public T[] toArray() return (T[]) new Object[] t; // if not Object[], what type?
Custom<String> custom = new Custom("hey");
String[] arr = custom.toArray(); // fails
Object[]
不是String[]
,因此无论演员阵容如何,都不能分配给一个Object[]
。同样的想法适用于Stream
和List
。使用重载的toArray(..)
方法。
【讨论】:
看看我的编辑。你能解释一下为什么会这样吗? 你的代码没问题,因为items
实际上是一个Integer[]
,而不是一个被转换成别的东西的Object[]
。
@MartijnRiemers 在您的示例中,您将返回已创建的数组(您知道其类型)。 Stream
(或List
)不能保证由数组支持。它可以通过任何其他方式构建。这意味着必须创建一个 new 数组,但我们不知道是哪种类型。
@MartijnRiemers ArrayList
的底层数组的真实(运行时)类型是Object[]
。
@MartijnRiemers 他们决定使用类型擦除来实现泛型。关于这是一个好的选择还是一个坏的选择,网上有很多讨论。你可能想看看他们。当R
是类型变量时,目前无法执行R.class
之类的操作。【参考方案2】:
关于toArray()
返回Object[]
的原因:是因为类型擦除。泛型类型在运行时会丢失它们的类型参数,因此Stream<Integer>
、Stream<String>
和Stream
成为相同的类型。因此无法确定要创建的数组的组件类型。其实可以通过反射分析数组元素的类型,然后尝试找到它们的least upper bound,但这太复杂和太慢了。
有一种方法可以通过使用重载的toArray(IntFunction<A[]> generator)
方法来获取R[]
数组。此方法使调用者有机会选择数组的类型。有关代码示例,请参阅此 SO 问题:How to Convert a Java 8 Stream to an Array?。
【讨论】:
【参考方案3】:试试:
String[] = IntStream.range(0, 10).mapToObj(Object::toString).toArray(String[]::new);
无参数的 toArray() 方法只会返回一个 Object[],但如果你传递一个数组工厂(可以方便地表示为数组构造函数引用),你可以获得任何你喜欢的(兼容的)类型。
【讨论】:
虽然这并不能直接解决 OP 的问题,但这是我一直在寻找的答案。以上是关于为啥新的 Java 8 流在 toArray 调用上返回一个对象数组?的主要内容,如果未能解决你的问题,请参考以下文章
为啥 OrderedQueryable 类的 ToArray 方法分配很多? [关闭]
java中Collection方法里面的Object[] toArray() 和 <T> T[] toArray(T[] a)有啥区别吗?