9

For various reasons I want to convert a list to an array, however the Collection contains objects that are themselves generics.

I have tried the following four options to get it to compile without needing a @supressWarnings('unchecked') annotation, but none of them work. Is there a solution to make this work correctly, or am I forced to use the annotation?

Iterator<T>[] iterators;
final Collection<Iterator<T>> initIterators = new ArrayList<Iterator<T>>();

// Type safety: Unchecked cast from Iterator[] to Iterator<T>[]
iterators = initIterators.<Iterator<T>>toArray(
        (Iterator<T>[])new Iterator[initIterators.size()]);

// Type safety: Unchecked invocation toArray(Iterator[]) of the generic 
// method toArray(T[]) of type Collection<Iterator<T>>
// Type safety: The expression of type Iterator[] needs unchecked conversion 
// to conform to Iterator<T>[]
iterators = initIterators.<Iterator<T>>toArray(
        new Iterator[initIterators.size()]);

// Type safety: The expression of type Iterator[] needs unchecked conversion 
// to conform to Iterator<T>[]
iterators = initIterators.toArray(new Iterator[initIterators.size()]);

// Doesn't compile
iterators = initIterators.toArray(new Iterator<T>[initIterators.size()]);
Stephen C
  • 632,615
  • 86
  • 730
  • 1,096
Paul Wagland
  • 23,951
  • 9
  • 48
  • 72
  • Maybe [[this](http://www.angelikalanger.com/GenericsFAQ/FAQSections/ParameterizedTypes.html#FAQ104)] will help a little. Anyway I am also interested why someone would like to use Arrays (which don't support generics) instead of lets say ArrayList. – Pshemo May 19 '13 at 02:22
  • possible duplicate of [Array of Generic List](http://stackoverflow.com/questions/7810074/array-of-generic-list). See also: [Generic types in an array for a tree with more than one child](http://stackoverflow.com/questions/15957325/generic-types-in-an-array-for-a-tree-with-more-than-one-child). – Paul Bellora May 19 '13 at 03:38
  • @PaulBellora I think that the questions that you list are not duplicates, although they do talk about similar material. – Paul Wagland May 19 '13 at 08:42
  • @Pshemo This is needed for dealing with some legacy code that expects an array. Some of the new code passes around lists, and before generics they could be converted between the two with impunity. I still can, of course, I just want to get rid of the warning, without suppressing it. – Paul Wagland May 19 '13 at 08:45
  • Duplicates are often chosen not because the questions themselves are exactly the same, but because their answers provide the same solutions. I know that might not make sense right away and it took me a while to get used to. But anyway, if you're using Java 7 you should checkout [this answer](http://stackoverflow.com/a/7810391/697449) on the linked post. I linked to the second post not because it's a duplicate but because my answer there talks about why this compiler restriction exists. – Paul Bellora May 19 '13 at 15:47
  • 1
    @PaulWagland If your legacy code is expecting an `Iterator[]`, then the Java 5+ "generics" version of that is `Iterator>[]` and the two are compatible. Just be aware that the array will allow ANY `Iterator` as an element, so if the legacy code ever _returns_ back to you an `Iterator[]` then you CANNOT safely (from the compiler's point of view) cast the elements back to `Iterator`. If you are _absolutely certain_ that the legacy code doesn't mix iterators for different types, that's the reason that the `@SuppressWarnings("unchecked")` annotation exists. Use it only with legacy code. – William Price May 20 '13 at 22:32

1 Answers1

12

There is no type-safe way to create an array of a parameterized type such as Iterator<T>[].

Alternatively, you can create a raw array: Iterator<?>[]. Or, if you can avoid the use of arrays entirely, use a collection type like List<Iterator<T>>.

The reason it is not possible is that Java arrays are covariant and the parameterized bounds of Generic types are invariant. That is to say:

Integer[] integers = new Integer[1];
Number[] numbers = integers; // OK because Integer extends Number
numbers[0] = new Double(3.14); // runtime exception

The compiler allows the assignment because Double extends Number and the declared type of numbers is Number[]. But at runtime the actual array object instance is the original Integer[1] and arrays know the type of the objects they contain.

With generics, parameterized types are different. For one, due to compile-time type erasure they do not intrinsically know their runtime types.

List<Integer> integerList = new ArrayList<Integer>();

List<Number> numberList = integerList; // compiler error, prevents:
numberList.add(new Double(3.14)); // would insert a Double into integerList

Collection<Integer> integerCollection = integerList; // allowed
// OK because List extends Collection and the <type parameter> did not change

Collection<Number> numberCollection = integerList; // compiler error
// an "Integer" is a "Number"
// but "a collection of Integers" is more specific than "a collection of Numbers"
// and cannot be generally treated the same way and guarantee correct behavior

List<?> rawList = integerList; // allowed, but...
rawList.add(new Integer(42));  // compiler error, Integer is not a ... a what?

With generics, in Java, you are relying on the compiler (not the runtime) to validate that the generic types are correct and safe.

So while an Iterator<?>[] knows at runtime that it is an array that contains Iterator elements, the <T> in Iterator<T>[] is erased at compile time and the runtime has no way to know what it was supposed to be. So you get an unchecked warning.

William Price
  • 3,677
  • 1
  • 30
  • 46
  • Good explanation, but, unfortunately, it doesn't help me. I _have_ a Collection>, but I _need_ a Iterator[]. I want to convert the former to the latter, and I can do so, but not without a compiler warning. It is only this warning that I want to get rid of. – Paul Wagland May 19 '13 at 07:54
  • @PaulWagland: it is impossible to get a non-`null` value of type `Iterator[]` in any way without suppressing a warning somewhere (either in your code or in some function you call) – newacct May 19 '13 at 08:39
  • @newacct That helps, but isn't the answer that I wanted ;-) It is the answer that I was afraid of though. – Paul Wagland May 19 '13 at 08:47