I can see many answers showing how to solve problem, but only Stephen's answer is trying to explain why problem occurs so I will try to add something more on this subject. It is a story about possible reasons why Object[] toArray
wasn't changed to T[] toArray
where generics ware introduced to Java.
Why String[] stockArr = (String[]) stock_list.toArray();
wont work?
In Java, generic type exists at compile-time. At runtime informations about generic type (like in your case <String>
) are removed and replaced with Object
type (take a look at type erasure). That is why at runtime toArray()
have no idea about what precise type to use to create new array. so it uses Object
as safest type, because each class extends Object so it can safely store all kind of instances.
Now the problem is that you can't cast instance of Object[]
to String[]
.
Why? Take a look at this example: (lets assume that class B extends A
)
//B extends A
A a = new A();
B b = (B)a;
Although it will compile, at runtime we will see thrown ClassCastException
because instance held by a
reference is not of type B
(or its subtypes). Why is this not allowed? One of the reasons is that B could add new methods or fields which A
doesn't have, so it is possible that someone will try to use these new members via b
reference even if instance doesn't have (doesn't support) them. In other words we could end up trying to use data which doesn't even exist, which could lead to many problems. So to prevent this JVM throws exception, and stop further potentially dangerous code.
You could ask now "So why aren't we stopped even earlier? Shy code involving such casting is even compilable? Shouldn't compiler stop it?". Answer is: no because compiler can't know for sure what kind of instance reference a
will hold, and there is a chance that it will hold instance of class B
which will support interface of b
reference. Take a look at this example:
A a = new B();
// ^------ Here reference "a" holds instance of type B
B b = (B)a; // so now casting is safe, now JVM is sure that `b` reference can
// safely access all its methods or fields
But lets go back to your arrays example. As you can see in question, we can't cast instance of Object[]
array to more precise type String[]
like
Object[] arr = new Object[] { "ab", "cd" };
String[] arr2 = (String[]) arr;//ClassCastException will be thrown
Here problem is a little different. Now we are sure that String[]
array will not have additional fields or methods because every array support only:
[]
operator,
length
filed,
- methods inherited from Object supertype,
So it is not interface that is making it impossible. Problem is that Object[]
array beside Strings
can store any objects (for instance Integers
) so it is highly probable that some beautiful day we will end up trying to invoke method like strArray[i].substring(1,3)
on instance of Integer
class which doesn't have such method.
So to make sure that this situation will never happen in Java array references can hold only
- instances of array of same type as reference (reference
String[] strArr
can hold String[]
)
- instances of array of subtype (
Object[]
can hold String[]
because String
is subtype of Object
),
but can't hold
- array of supertype of type of array from reference (
String[]
can't hold Object[]
)
- array of type which is not related to type from reference (
Integer[]
can't hold String[]
)
In other words something like this is OK
Object[] arr = new String[] { "ab", "cd" }; //OK - because
// ^^^^^^^^ `arr` holds array of subtype of Object (String)
String[] arr2 = (String[]) arr; //OK - `arr2` reference will hold same array of same type as
// reference
You could say that one way to resolve this problem is to find at runtime most common type between all list elements and create array of that type, but this wont work in situations where all elements of list will be of one type derived from generic one. Take a look
//B extends A
List<A> elements = new ArrayList<A>();
elements.add(new B());
elements.add(new B());
now most common type is B
, not A
so toArray()
A[] arr = elements.toArray();
would return array of B
class new B[]
. Problem with this array is that while compiler would allow you to edit its content by adding new A()
element to it, you would get ArrayStoreException
because B[]
array can hold only elements of class B
or its subclass, to make sure that all elements will support interface of B
, but instance of A
may not have all methods/fields of B
. So this solution is not perfect.
Best solution to this problem is explicitly tell what type of array toArray()
should be returned by passing this type as method argument like
String[] arr = list.toArray(new String[list.size()]);
or
String[] arr = list.toArray(new String[0]); //if size of array is smaller then list it will be automatically adjusted.
String [] stockArr = (String[]) stock_list.toArray(new String[0]);
refer java doc here – Nishant Mar 21 '11 at 6:11String[] stockArr = stock_list.toArray(new String[0]);
is enough. – Peter Rader Dec 23 '14 at 8:06