First, note that arrays know their component type at runtime, but instances of generic classes don't know the generic type argument at runtime. A List<T>
cannot create an array object at runtime with the runtime type of T[]
without further information, since it doesn't know what T
is at runtime. The List#toArray()
method that takes one array parameter, uses the runtime type of the passed-in array instance to construct an array of the same component type at runtime. But the List#toArray()
with no parameters always creates an array with the runtime type Object[]
. So elements.toArray()
evaluates to an array instance that always has the runtime type of Object[]
.
Object[]
is not a subtype of T[]
(when T
is not Object
), so assigning this array to this.elements
, of compile-time type T[]
, is wrong. However, it doesn't immediately cause any exceptions, since the erasure of T
is Object
, so the erasure of T[]
is Object[]
, and assigning Object[]
to Object[]
is fine. It won't cause any issues as long as you make sure to never expose the object contained in this.elements
to outside this object as T[]
. However, if you expose the object contained in this.elements
as type T[]
to outside the class (e.g. a method that returns this.elements
as type T[]
, or if you make this.elements
a public or protected field), a caller outside the class might expect T
to be a specific type, and that can cause a class cast exception.
For example, if you have a method that returns this.elements
as type T[]
:
public T[] getElements() {
return this.elements;
}
and then you have a caller that holds ArrayTypeErasure<String>
and it calls .getElements()
on it, it will expect a String[]
. When it tries to assign the result to a String[]
, it will cause a class cast exception, since the runtime type of the object is Object[]
:
ArrayTypeErasure<String> foo = ...
String[] bar = foo.getElements();