12

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> ?

Paul Bellora
  • 54,340
  • 18
  • 130
  • 181
peter
  • 8,333
  • 17
  • 71
  • 94
  • what do you mean *we* could use `` instead of `T` - ? Are you asking why `ArrayList.toArray()` is defined like that ? or you are asking that while using an instance of an `ArrayList` why cant we automatically imply the `E` in the `toArray()` ? – Bhaskar Sep 10 '12 at 20:37
  • 3
    People always seem to ask about `toArray` with respect to `ArrayList`, but that method is actually [declared by `Collection`](http://docs.oracle.com/javase/7/docs/api/java/util/Collection.html#toArray\(T[]\)). – Paul Bellora Sep 11 '12 at 04:05
  • Yes you are right let me update the question – peter Sep 11 '12 at 18:38

2 Answers2

12

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.

Community
  • 1
  • 1
Paul Bellora
  • 54,340
  • 18
  • 130
  • 181
  • I think you mean "if I called `toArray` on an `ArrayList` and passed in an `Number[]`" because `Number` is a super type of `Integer`. Aside from that, I am having trouble following. Can you provide an example of an `ArrayList` where `toArray` into a `Number[]` fails at runtime? – Daniel Trebbien Jan 09 '13 at 22:58
  • @DanielTrebbien Thanks for asking. I meant exactly what I wrote, as an example of something that *seems* like it wouldn't compile, until you remember that an `Integer[]` is also a `Number[]` due to array covariance. It's a hypothetical example in which we pretend the syntax `` is legal (it isn't). – Paul Bellora Jan 09 '13 at 23:30
  • @DanielTrebbien As for the example you asked for, consider that a `Long[]` is also a `Number[]`. So I could call `toArray` on an `ArrayList` and pass in a `Long[]` - it would compile but fail at runtime with an `ArrayStoreException`. – Paul Bellora Jan 09 '13 at 23:33
  • Aha! I get what you are saying now. Thanks. – Daniel Trebbien Jan 10 '13 at 00:03
6

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.

Paul Bellora
  • 54,340
  • 18
  • 130
  • 181
John B
  • 32,493
  • 6
  • 77
  • 98
  • What do you mean by your first sentence ? – peter Sep 10 '12 at 16:48
  • Take `Integer`, it extends `Number`. If you had a `List` but needed to convert it to a `Number[]`, having the type returned by `toArray` be `T` would limit it to only returning `Integer[]`. The current signature allows it to return any type to which `Integer` can be cast (Number, Comparable, etc). – John B Sep 10 '12 at 16:54
  • So the purpose of using a separate generic type T is mainly for preventing that ? – peter Sep 10 '12 at 17:03
  • 1
    I think you got E and T reversed. But anyway, the way that the method is declared, the two type variables have no constraint with one another. So you could ask for an array of *any type* (not just a supertype). I guess this is okay since arrays are checked at runtime, and your elements might really be of that type. But it means less compile-time checks than you think. – newacct Sep 10 '12 at 19:14
  • Arrays are already covariant. `(Number[]) new Integer[0]` is valid. If `T` had to be a super class of `E`, it would use a `super` bound i.e. `T super E`. – obataku Sep 11 '12 at 04:35