为什么 Java 的 Collection.toArray() 返回 Object[] 而不是 E[]?
在 Java 泛型出现之前,Collection.toArray()
无法知道开发人员需要哪种类型的数组(特别是对于空集合)。据我了解,这是习语 collection.toArray(new E[0])
背后的主要原理。
对于泛型,Collection
只能返回一个充满 E
实例和/或其特化的数组。我想知道为什么返回类型仍然是 Object[]
而不是 E[]
。在我看来,返回 E[]
而不是 Object[]
不应破坏现有代码。
请参阅:集合.toArray()
, 集合.toArray(T[])
和相关主题 java: (String[])List.toArray() 给出 ClassCastException
Before Java generics, Collection.toArray()
had no way to know which type of array the developer expected (particularly for an empty collection). As I understand it, this was the main rationale behind the idiom collection.toArray(new E[0])
.
With generics, Collection<E>.toArray()
can only return an array full of instances of E
and/or its specialisations. I wonder why the return type still is as Object[]
rather than E[]
. In my opinion, returning an E[]
instead of Object[]
should not break existing code.
See: Collection.toArray()
, Collection.toArray(T[])
and the related topic java: (String[])List.toArray() gives ClassCastException
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
这是一个非常好的问题。答案是仿制药也称为“擦除”。它不仅仅是一个名字。泛型编码的信息仅在编译时使用,然后被删除。因此,JVM 甚至不知道这个泛型类型
E
,因此它无法创建数组E[]
。其他方法 toArray(T[] a) 在运行时从参数接收有关类型的信息。这就是该方法的原型为
的原因。 T[] toArray(T[] a)
:它获取类型为T的数组,并且可以返回类型为T的数组。类型作为参数传递。It is a very good question. The answer is that generics are also called "erasures." It is not just a name. The information coded by generics is used at compile time only and then is removed. So, JVM even does not know this generic type
E
, so it cannot create arrayE[]
.Other method
toArray(T[] a)
receives the information about the type from the argument at runtime. This is the reason this method's prototype is<T> T[] toArray(T[] a)
: it gets array of type T and can return array of type T. The type is passed as a parameter.“类型擦除”只是部分解释:
Collection
及其toArray()
方法在运行时都没有任何有关E
的信息。也正是因为向后兼容,
Collection.toArray()
仍必须返回Object[]
。在 Java 1.5 之前,无法知道集合的泛型类型,因此这是唯一合理的 API 设计。"Type erasure" is only a partial explanation: Neither the
Collection
, nor itstoArray()
method have any information aboutE
at run time.It is also because of backwards compatibility, that
Collection.toArray()
must still returnObject[]
. Before Java 1.5, there was no way of knowing a generic type for a collection, so this was the only reasonable API design.@Lukas,关于:“new E[]”
new E[0] 引发了编译器错误,正如您可能预期的那样。我找到的解决方法是:
Final E[] returnArray = (E[]) events.toArray( new Event[ events.size() ] );
注意,代码位于模板类 Listener 中。 E 扩展事件>。
在我的解决方法中,类型擦除既是问题也是解决方案。转换为 (E[]) 是安全的,因为它的精确类型被擦除为 Event[]。我看到的唯一缺点是编译器关于“未经检查或不安全操作”的警告(显然,在这种情况下,强制转换不是给定类型擦除的)。
@Lukas,关于向后兼容性,
我认为向后兼容性没有大问题。使返回类型更特殊与使参数类型更特殊不同。
换句话说,到目前为止期望 Collection.toArray() 返回 Object[] 的源代码应该非常高兴收到 E[]。
至于字节码,由于类型擦除,Object[] 和 E[] 无论如何都是相同的。
@Lukas, regarding: “new E[]”
The new E[0] raised the comiler error, as you probably expected. The workaround I have found is:
final E[] returnArray = (E[]) events.toArray( new Event[ events.size() ] );
N.B. the code is in a template class Listener<E extends Event>.
In my workaround, type erasure is both the problem and the solution. The cast to (E[]) is safe because its precise type is erased to Event[]. The only downside I see is the compiler warning about “unchecked or unsafe operations” (which, obviously, the cast is not in this case given type erasure).
@Lukas, regarding backward compatibility
I do not see a big problem with backward compatibility. Making the return type more special is not the same as making the argument type more special.
In other words, source code which so far expected Collection.toArray() to return an Object[] should be perfectly happy to receive an E[] instead.
And as to byte code, the Object[] and E[] are anyway the same due to type erasure.