java: (String[])List.toArray() gives ClassCastException
Asked Answered
M

5

118

The following code (run in android) always gives me a ClassCastException in the 3rd line:

final String[] v1 = i18nCategory.translation.get(id);
final ArrayList<String> v2 = new ArrayList<String>(Arrays.asList(v1));
String[] v3 = (String[])v2.toArray();

It happens also when v2 is Object[0] and also when there are Strings in it. Any Idea why?

Madelle answered 16/4, 2011 at 23:25 Comment(3)
You may want to read about Covariance and Contravariance -- en.wikipedia.org/wiki/…Tapp
What about the case where T is an interface with a factory method for instantiation.Sanbenito
@LaceCard - this is only very indirectly related to covariance/contravariance. The real issue is that this is a direct consequence of the specified behaviour of the toArray() method.Loudish
B
266

This is because when you use

 toArray() 

it returns an Object[], which can't be cast to a String[] (even tho the contents are Strings) This is because the toArray method only gets a

List 

and not

List<String>

as generics are a source code only thing, and not available at runtime and so it can't determine what type of array to create.

use

toArray(new String[v2.size()]);

which allocates the right kind of array (String[] and of the right size)

Bouldon answered 16/4, 2011 at 23:26 Comment(3)
@Kaleb - not true. Or at least that's not the original reason. The toArray methods existed before the collection classes were generic.Loudish
This is the correct solution, but not the correct explanation. You can't cast Object[] to Double[] because it's a language feature, nothing more. It doesn't have to do with Generics. You can cast Object to Double assuming that's it is truly a Double. So logically, you could do the same with an array, but it's simply not part of the language. If you cast Object to Double there will be a runtime check to make sure Object actually IS a Double. If you cast Double to Object there is no runtime check, since Object is in the inheritance hierarchy of Double.Bratcher
When doing this in IntelliJ, I get a warning to use toArray(new String[0]) rather: "In older Java versions using pre-sized array was recommended as the reflection call which is necessary to create an array of proper size was quite slow. However since late updates of OpenJDK 6 this call was intrinsified, making the performance of the empty array version the same and sometimes even better. Also passing pre-sized array is dangerous for a concurrent collection as a race is possible between the size and toArray call."Ferrell
F
36

You are using the wrong toArray()

Remember that Java's generics are mostly syntactic sugar. An ArrayList doesn't actually know that all its elements are Strings.

To fix your problem, call toArray(T[]). In your case,

String[] v3 = v2.toArray(new String[v2.size()]);

Note that the genericized form toArray(T[]) returns T[], so the result does not need to be explicitly cast.

Fink answered 16/4, 2011 at 23:59 Comment(0)
B
2

Using toArray from the JDK 11 Stream API, you can solve the more general problem this way:

Object[] v1 = new String[] {"a", "b", "c"}; // or array of another type
String[] v2 = Arrays.stream(v1)
    .<String>map((Object v) -> v.toString()).toArray(String[]::new);
Breakneck answered 7/8, 2021 at 11:29 Comment(0)
W
1
String[] v3 = v2.toArray(new String[0]); 

also does the trick, note that you don't even need to cast anymore once the right ArrayType is given to the method.

Wittie answered 28/11, 2019 at 21:55 Comment(0)
S
0
String[] str = new String[list.size()];
str = (String[]) list.toArray(str);

Use like this.

Scutari answered 9/2, 2019 at 9:41 Comment(1)
Please! Format your code! Select all of it at press "ctrl + k", or add " ` " before the first letter of code and at the last one another one. You can also select "{}" in the help at the top when writing the answer!Bane

© 2022 - 2024 — McMap. All rights reserved.