23
votes

I came across something very basic but extremely bewildering today. I needed to convert a list to an array. The list contained String instances. Perfect example of using List.toArray(T[]), since I wanted a String[] instance. It would not work, however, without explicitly casting the result to String[].

As a test scenario, I used the following code:

import java.util.Arrays;
import java.util.List;

public class MainClass {
    public static void main(String args[]) {
        List l = Arrays.asList("a", "b", "c");
        String stuff[] = l.toArray(new String[0]);
        System.err.println(Arrays.asList(stuff));
    }
}

which does not compile. It's nearly an exact copy of the example in the javadoc, yet the compiler says the following:

MainClass.java:7: incompatible types
found   : java.lang.Object[]
required: java.lang.String[]
    String stuff[] = l.toArray(new String[0]);
                          ^

If I add a cast to String[] it will compile AND run perfectly. But that is not what I expect when I looked at the signature of the toArray method:

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

This tells me that I shouldn't have to cast. What is going on?

Edit:

Curiously, if I change the list declaration to:

List<?> l = Arrays.asList("a", "b", "c");

it also works. Or List<Object>. So it doesn't have to be a List<String> as was suggested. I am beginning to think that using the raw List type also changes how generic methods inside that class work.

Second edit:

I think I get it now. What Tom Hawtin wrote in a comment below seems to be the best explanation. If you use a generic type in the raw way, all generics information from that instance will be erased by the compiler.

5

5 Answers

29
votes

you forgot to specify the type parameter for your List:

List<String> l = Arrays.asList("a", "b", "c");

in this case you can write safety:

String[] a = l.toArray(new String[0]);

without any cast.

3
votes

or do

List<?> l = Arrays.asList("a", "b", "c");  

still strange

1
votes
List<String> l = Arrays.asList("a", "b", "c");

this will make it compile, you're using generics to say "this is a list of Strings" so the toArray method knows which type of array to return.

0
votes

That's because your list contains objects, not strings. Had you declared your list as List<String>, the compiler would be happy.

0
votes

List without a declared type will default to "List of Object" while List<?> means "List of unknown". In the world of generic types it is a fact that "List of Object" is different from "List of String" but the compiler cannot say the same for "List of unknown". If you've declared it as unknown then, as far as the compiler can tell, that's OK.

The main point is that declaring something as the wildcard ? is different from declaring it as Object. Read more about wildcards here