Why does Java's Collection.toArray() return

2019-01-25 03:28发布

问题:

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

回答1:

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 array E[].

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.



回答2:

"Type erasure" is only a partial explanation: Neither the Collection, nor its toArray() method have any information about E at run time.

It is also because of backwards compatibility, that Collection.toArray() must still return Object[]. Before Java 1.5, there was no way of knowing a generic type for a collection, so this was the only reasonable API design.



回答3:

@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.