Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why Collection.toArray(T[]) doesn't take an E[] instead

The toArray method (lets pick the implementation in java.util.ArrayList) is the following:

class ArrayList<E> ....{
    public <T> T[] toArray(T[] a){
        if(a.length < size)
            return (T[]) Arrays.copyof(elementData, size, a.getClass());
        System.arraycopy(elementData, 0, a, 0, size);
        if(a.length > size)
            a[size] = null;
        return a;
    }    
}

I am wondering could we use <E> instead of <T> in this case ? like

public E[] toArray(E[] a){
      if(a.length < size)
             return (E[]) Arrays.copyof(elementData, size, a.getClass());
      System.arraycopy(elementData, 0, a, 0, size);
      if(a.length > size)
            a[size] = null;
      return a;
}    

Since the ArrayList class iteself is already generic to <E>, so could we use that instead of a new generic type <T> ?

like image 653
peter Avatar asked Sep 10 '12 16:09

peter


2 Answers

I think John B's answer covered the idea well - I'd just like to elaborate on it a little.

First, let's look at the method signature you proposed in your question:

public E[] toArray(E[] a)

As John explained, this signature is less flexible. If I want to dump an ArrayList<Integer> into a Number[], this method doesn't let me. The Collections API wants to allow that flexibility.

Unfortunately the method as it stands doesn't allow for compile-time checking on the type of the array, which is the issue you seem to be getting at. The documentation for toArray, which is declared by the Collection, explains that an ArrayStoreException may be thrown "if the runtime type of the specified array is not a supertype of the runtime type of every element in this collection".

Based on that description, it seems like the following signature would be ideal:

public <T super E> T[] toArray(T[] a)

At first glance, this seems like it would allow any legal type of array to be passed in and populated - but would provide type checking at compile time instead of run time. So why is this signature not declared instead?

Well, this syntax:

 <T super E>

is not supported by the language. But it's easy for that to distract you from the fact that this type checking wouldn't work anyway. The problem is that unlike parameterized types, arrays are covariant. An Integer[] is a Number[] is an Object[]. So given our hypothetical signature with <T super E>, if I called toArray on an ArrayList<Number> and passed in an Integer[], it would still compile - and possibly fail at runtime depending on what was in the list.

So the bottom line is, a lower bound on the component type of the array passed in isn't going to do any good.

like image 60
Paul Bellora Avatar answered Oct 13 '22 21:10

Paul Bellora


The point of the <T> is if the array desired is of a base class of E. For example if E is HashMap but the desired array was Map[]. If toArray were locked down to E this would not be possible.

This type of thing is not needed in generic collections / types due to type-erasure. But there is no type-erasure with arrays so the type of the array can be very important.

like image 21
John B Avatar answered Oct 13 '22 21:10

John B