3

Consider the following test of Java's ArrayList#toArray method. Note that I borrowed the code from this helpful answer.

public class GenericTest {
    public static void main(String [] args) {
        ArrayList<Integer> foo = new ArrayList<Integer>();
        foo.add(1);
        foo.add(2);
        foo.add(3);
        foo.add(4);
        foo.add(5);
        Integer[] bar = foo.toArray(new Integer[10]);
        System.out.println("bar.length: " + bar.length);

        for(Integer b : bar) { System.out.println(b); }

        String[] baz = foo.toArray(new String[10]);       // ArrayStoreException
        System.out.println("baz.length: " + baz.length);
    }
}

But, notice that there will be a ArrayStoreException when trying to put an Integer into a String[].

output:

$>javac GenericTest.java && java -cp . GenericTest
bar.length: 10
1
2
3
4
5
null
null
null
null
null
Exception in thread "main" java.lang.ArrayStoreException
        at java.lang.System.arraycopy(Native Method)
        at java.util.ArrayList.toArray(Unknown Source)
        at GenericTest.main(GenericTest.java:16)

Can this error be prevented through Java generics at compile-time?

Community
  • 1
  • 1
Kevin Meredith
  • 41,036
  • 63
  • 209
  • 384

4 Answers4

7

ArrayStoreException exists precisely because Java's type system cannot handle this situation properly (IIRC, by the time Generics came along, it was too late to retrofit arrays in the same manner as the collections framework).

So you can't prevent this problem in general at compile time.

You can of course create internal APIs to wrap such operations, to reduce the likelihood of accidentally getting the types wrong.

See also:

Community
  • 1
  • 1
DNA
  • 42,007
  • 12
  • 107
  • 146
  • You write "because Java's type system cannot handle this situation properly". What is the explanation for that? – mrbela May 07 '19 at 12:54
  • There's quite a lot of detail and background in the linked question ("Why are arrays covariant...") but essentially it's for historical reasons that made sense at the time... – DNA May 07 '19 at 16:15
2

List#toArray(T[]) is a generic method declared as

<T> T[] toArray(T[] a);

So the type argument is either inferred from the type of the given array or with the <Type> notation prefixing the method invocation.

So you could do

String[] baz = foo.<Integer>toArray(new String[10]); // doesn't compile

But I think that's the best you could do.

But in that sense, you can clearly see that Integer doesn't match String (or vice-versa).

Note that this is a documented exception

ArrayStoreException - if the runtime type of the specified array is not a supertype of the runtime type of every element in this list

So I don't think you should be trying to find it at compile time.

Sotirios Delimanolis
  • 274,122
  • 60
  • 696
  • 724
0

The method Collection.toArray cannot be changed for compatibility reasons.

However for your own code you can create a (more) type-safe helper method which protects you from the ArrayStoreException if you use your method consequently:

public static <T> T[] toArray(List<? extends T> list, T[] t) {
    return list.toArray(t);
}

This method will reject String[] s=toArray(new ArrayList<Integer>(), new String[0]); which matches the example case of your question, but beware of the array subtyping rule: it will not reject

Object[] s=toArray(new ArrayList<Integer>(), new String[0]);

because of the pre-Generics “String[] is a subclass of Object[]” rule. This can’t be solved with the existing Java language.

Holger
  • 285,553
  • 42
  • 434
  • 765
0

The ArrayStoreException is runtime exception not compile time and thrown at run time and it indicates that different type of object is being stored in the array. Object x[] = new String[3]; x[0] = new Integer(0); The only way you can find it at compile time is by using <Integer> type as below

foo.<Integer>toArray(new String[10]); 

The above will throw compile time error as The parameterized method <Integer>toArray(Integer[]) of type List<Integer> is not applicable for the arguments (String[]).

hi.nitish
  • 2,732
  • 2
  • 14
  • 21