super.t
super.t

Reputation: 2736

Array type erasure

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

Answers (3)

newacct
newacct

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

daniu
daniu

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

Yassin Hajaj
Yassin Hajaj

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

Related Questions