Kevin Meredith
Kevin Meredith

Reputation: 41939

Catching ArrayStoreException at Compile-time

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?

Upvotes: 3

Views: 675

Answers (4)

hi.nitish
hi.nitish

Reputation: 2984

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[]).

Upvotes: 0

Holger
Holger

Reputation: 298579

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.

Upvotes: 0

DNA
DNA

Reputation: 42617

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:

Upvotes: 7

Sotirios Delimanolis
Sotirios Delimanolis

Reputation: 280181

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.

Upvotes: 2

Related Questions