Reputation: 2736
Why the following code is potentially not type-safe (the compiler generates a warning)?
class ArrayTypeErasure<T> {
private T[] elements;
public void setElements(List<T> elements) {
this.elements = (T[]) elements.toArray();
}
}
I'm trying to think of any situations when the code would fail, but so far it's only me who is failing.
Upvotes: 0
Views: 258
Reputation: 122419
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();
Upvotes: 2
Reputation: 14999
Due to type erasure, you can cast List<X>
s to List<Y>
s.
ArrayTypeErasure<String> er = new ArrayTypeErasure<>();
ArrayTypeErasure erased = er;
List intList = new List<Integer>(); // compile warnings, but
intList.add(1);
erased.setElements(intList);
Upvotes: -2
Reputation: 21965
Simply because List#toArray()
returns an Object[]
so there is no guarantee from this method that it would return T[]
Now, in practice, it's ok since you always know that it will return the wanted type
You could use @SuppressWarnings("unchecked")
to avoid this warning from appearing
Upvotes: 2