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 correctlyThis 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 runtimeThat’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]): UnitThe 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.