Stack Overflow is a community of 4.7 million programmers, just like you, helping each other.

Join them; it only takes a minute:

Sign up
Join the Stack Overflow community to:
  1. Ask programming questions
  2. Answer and help your peers
  3. Get recognized for your expertise

Say you have an arraylist defined as follows:

ArrayList<String> someData = new ArrayList<>();

Later on in your code, because of generics you can say this:

String someLine = someData.get(0);

And the compiler knows outright that it will be getting a string. Yay generics! However, this will fail:

String[] arrayOfData = someData.toArray();

toArray() will always return an array of Objects, not of the generic that was defined. Why does the get(x) method know what it is returning, but toArray() defaults to Objects?

share|improve this question
    
What are you talking about? Which class has the toArray() method? – f1sh yesterday
    
ArrayList has a toArray() method, but even when you define the generic, the toArray() method will return Object[], not E[], which is implied using the generic. – blahfunk yesterday
8  
Instead of overriding toArray() you can use toArray(T[] a) method to get appropriate array. – Sudhir Singh yesterday
3  
Yes, I know I can to toArray(T[] a), but why is it that it wasn't just built directly into toArray(). I don't understand why get(x) knows what it is giving out but toArray() does not. – blahfunk yesterday
1  
@JimmyB: an alternative is some kind of factory for the dynamic type, e.g. Java 8’s Stream.toArray uses IntFunction<ArrayType> as factory type, passed as parameter. The reason this pattern hasn’t been used before, e.g. for the Collection interface, is that only Java 8 allows to implement it as neat as ElementType[]::new, allowing, e.g. String[] array = stream.toArray(String[]::new) – Holger yesterday
up vote 31 down vote accepted

If you look at the implementation of toArray(T[] a) of ArrayList<E> class it is like:

    public <T> T[] toArray(T[] a) {
        if (a.length < size)
            // Make a new array of a's runtime type, but my contents:
            return (T[]) Arrays.copyOf(elementData, size, a.getClass());
        System.arraycopy(elementData, 0, a, 0, size);
        if (a.length > size)
            a[size] = null;
        return a;
    }

Problem with this method is that you need to pass array of the same generic type. Now consider if this method do not take any argument then the implementation would be something similar to:

    public <T> T[] toArray() {
        T[] t = new T[size]; // compilation error
        return Arrays.copyOf(elementData, size, t.getClass());
    }

But the problem here is that you can not create a generic arrays in Java because compiler does not know exactly what T represents. In other words creation of array of a non-reifiable type (JLS §4.7) is not allowed in Java.

Another important quote from Array Store Exception (JLS §10.5):

If the component type of an array were not reifiable (§4.7), the Java Virtual Machine could not perform the store check described in the preceding paragraph. This is why an array creation expression with a non-reifiable element type is forbidden (§15.10.1).

That is why Java has provided overloaded version toArray(T[] a).

I will override the toArray() method to tell it that it will return an array of E.

So instead of overriding toArray(), you should use toArray(T[] a).

Cannot Create Instances of Type Parameters from Java Doc might also be interesting for you.

share|improve this answer
    
Thank you. This explains it in black and white why it fails. – blahfunk yesterday
    
In short, Java does not allow a method to be polymorphic (i.e. generic) in (only) its return value. An example of a language which does support this is Haskell. – jpaugh yesterday
2  
I think you captured the crux of the problem which is simply that Java cannot create arrays of generic types. – Pace 22 hours ago
5  
@jpaugh Java does allow that, and it even infers the type of the RHS of an assignment based on the LHS. You can also explicitly declare the type parameters when invoking a generic method. See, for instance, ideone.com/AObtRR. – JohannesD 13 hours ago
    
@JohannesD I stand corrected. (Thanks!) Examples like toArray and your link to getnull show that Java comes so close, and just misses the mark. Then again, Haskell does not even attempt subtyping, so its hard to compare their generics support one-for-one. – jpaugh 5 hours ago

Generic information is erased at runtime. JVM does not know whether your list is List<String> or List<Integer> (at runtime T in List<T> is resolved as Object), so the only possible array type is Object[].

You can use toArray(T[] array) though - in this case JVM can use the class of a given array, you can see it in the ArrayList implementation:

public <T> T[] toArray(T[] a) {
    if (a.length < size)
        // Make a new array of a's runtime type, but my contents:
        return (T[]) Arrays.copyOf(elementData, size, a.getClass());
share|improve this answer
3  
Your answer is correct but not easy to understand for non-Java experts. The important fact is that the returned array has to be created at runtime (-> missing info of what type), where as get(..) just returns an existing object. – Robert yesterday

If you look at the Javadoc for the List interface, you'll notice a second form of toArray: <T> T[] toArray(T[] a).

In fact, the Javadoc even gives an example of how to do exactly what you want to do:

String[] y = x.toArray(new String[0]);

share|improve this answer

I can, and will use an iterator instead of making an array sometimes, but this just always seemed strange to me. Why does the get(x) method know what it is returning, but toArray() defaults to Objects? Its like half way into designing it they decided this wasn't needed here??

As the intention of the question seems to be not just about getting around using toArray() with generics, rather also about understanding the design of the methods in the ArrayList class, I would like to add:

ArrayList is a generic class as it is declared like

public class ArrayList<E> extends AbstractList<E>
    implements List<E>, RandomAccess, Cloneable, java.io.Serializable

which makes it possible to use Generic methods such as public E get(int index) within the class.

But if a method such as toArray() is not returning E, rather E[] then things start getting a bit tricky. It would not be possible to offer a signature such as public <E> E[] toArray() because it is not possible to create generic arrays.

Creation of arrays happen at runtime and due to Type erasure, Java runtime has no specific information of the type represented by E. The only workaround as of now is to pass the required type as a parameter to the method and hence the signature public <T> T[] toArray(T[] a) where clients are forced to pass the required type.

But on the other hand, it works for public E get(int index) because if you look at the implementation of the method, you would find that even though the method makes use of the same array of Object to return the element at the specified index, it is casted to E

E elementData(int index) {
    return (E) elementData[index];
}

It is the Java compiler which at the compile time replaces E with Object

share|improve this answer

The pertinent thing to note is that arrays in Java know their component type at runtime. String[] and Integer[] are different classes at runtime, and you can ask arrays for their component type at runtime. Therefore, a component type is needed at runtime (either by hard-coding a reifiable component type at compile time with new String[...], or using Array.newInstance() and passing a class object) to create an array.

On the other hand, type arguments in generics do not exist at runtime. There is absolutely no difference at runtime between an ArrayList<String> and a ArrayList<Integer>. It is all just ArrayList.

That's the fundamental reason why you can't just take a List<String> and get a String[] without passing in the component type separately somehow -- you would have to get component type information out of something that doesn't have component type information. Clearly, this is impossible.

share|improve this answer

An array is of a different type than the type of the array. It's sort of StringArray class instead of String class.

Assuming, it would be possible, an Generic method toArray() would look like

private <T> T[] toArray() {
    T[] result = new T[length];
    //populate
    return result;
}

Now during compilation, the type T gets erased. How should the part new T[length] be replaced? The generic type information is not available.

If you look at the source code of (for example) ArrayList, you see the same. The toArray(T[] a) method either fills the given array (if the size matches) or creates a new new array using the type of the parameter, which is the array-type of the Generic Type T.

share|improve this answer
    
How does the get(x) method get around this? It knows what it is giving back – blahfunk yesterday
    
It doesn't, at runtime get(x) returns Object. This is just hidden from you by the compiler implicity inserting casts. – plugwash yesterday

The very first thing you have to understand is what ArrayList own is just an array of Object

   transient Object[] elementData;

When it comes to the reason why T[] is fail, it because you can't get an array of generic type without a Class<T> and this is because java's type erase( there is a more explanation and how to create one). And the array[] on the heap knows its type dynamically and you can't cast int[] to String[]. The same reason, you can't cast Object[] to T[].

   int[] ints = new int[3];
   String[] strings = (String[]) ints;//java: incompatible types: int[] cannot be converted to java.lang.String[]

   public <T> T[] a() {
      Object[] objects = new Object[3];
      return (T[])objects;
   }
   //ClassCastException: [Ljava.lang.Object; cannot be cast to [Ljava.lang.Integer;
   Integer[] a = new LearnArray().<Integer>a();

But what you put into the array is just a object which type is E(which is checked by compiler), so you can just cast it to E which is safe and correct.

  return (E) elementData[index];

In short, you can't get what don't have by cast. You have just Object[], so toArray() can just return Object[](otherwise, you have to give it a Class<T> to make a new array with this type). You put E in ArrayList<E>, you can get a E with get().

share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.