Answer: Why is toArray() a Generic Method in Java Collections?

In a previous post, I asked why Collection.toArray() is declared as a generic method when the collection itself is already parameterized. Thanks to Joshua Marotti and Kevin from CinJUG for helping me think through this.

The Key Scenario

Consider this:

class Bar {}
class Foo extends Bar {}

List<Foo> foos = new ArrayList<>();
foos.add(new Foo());
foos.add(new Foo());

Bar[] bars = new Bar[foos.size()];
bars = foos.toArray(bars);  // Works correctly

This works because toArray(T[] a) lets the caller pass in a Bar[], and the method signature <T> T[] toArray(T[] a) is flexible enough to accommodate that. If toArray used only the collection’s own type parameter E, this upcasting would be impossible at compile time.

The Runtime Trade-off

The generic method approach doesn’t prevent all misuse:

String[] strings = new String[foos.size()];
strings = foos.toArray(strings);  // Compiles — but throws ArrayStoreException at runtime

That’s a real limitation: the compiler can’t catch this, only the runtime can.

PECS: Why This Makes Sense

Joshua Bloch’s PECS guideline — Producer Extends, Consumer Super — explains the broader pattern. Collection constructors use extends:

public ArrayList(Collection<? extends E> c)

Because the collection produces elements for the new list. For toArray, the array is a consumer, which would ideally use super. But Java’s type system doesn’t allow lower bounds on method type parameters:

// Ideal, but Java doesn't support this:
<T super E> T[] toArray(T[] a)

Scala Gets It Right

Interestingly, Scala handles this cleanly:

def copyToArray[B >: A](xs: Array[B]): Unit

The B >: A constraint (B is a supertype of A) is exactly what T super E would express in Java — Scala’s type system is expressive enough to state it directly.

A nice reminder that language design choices have long-lasting practical consequences.