I have the following class (simplified but still a working example):
class Test<T> {
List<T> l = new ArrayList<>();
public Test() {
}
public void add(Object o) {
l.add((T)o);
}
}
And the test code:
Test<Double> t = new Test<>();
t.add(1);
t.add(1.2);
t.add(-5.6e-2);
t.add("hello");
Everything is working fine, and that's not what I was expecting. Shouldn't the add
method throw a ClassCastException
? If I add a get
method that's more or less the same thing:
public T get(int i) {
return l.get(i);
}
.../...
t.get(1); // OK.
t.get(3); // OK (?)
Double d = t.get(3); // throws ClassCastException
Why is it only on variable assignment that the exception is thrown? How can I enforce type consistency if the (T)
cast doesn't work?
Shouldn't the add method throw a
ClassCastException
?
No, it shouldn't (although I wish it did). Long story short, Java implementation of generics discards type information after compiling your code, so List<T>
is allowed to take any Object
, and the cast inside your add
method is not checked.
Why is it only on variable assignment that the exception is thrown?
Because the cast to Double
there is inserted by the compiler. Java compiler knows that the return type of get
is T
, which is Double
, so it inserts a cast to match the type of the variable d
, to which the result is being assigned.
Here is how you can implement a generic-safe cast:
class Test<T> {
private final Class<T> cl;
List<T> l = new ArrayList<>();
public Test(Class<T> c) {
cl = c;
}
public void add(Object o) {
l.add(cl.cast(o));
}
}
Now the cast is performed by a Class<T>
object, so you will get a ClassCastException
on an attempt to insert an object of an incorrect type.
As an alternative solution you can use Collections.checkedList
:
class Test<T> {
List<T> l;
public Test(Class<T> c) {
l = Collections.checkedList(new ArrayList<T>(), c);
}
public void add(Object o) {
l.add((T) o);
}
}
This way you will get the following exception:
Exception in thread "main" java.lang.ClassCastException: Attempt to insert
class java.lang.Integer element into collection with element type class java.lang.Double
at java.util.Collections$CheckedCollection.typeCheck(Collections.java:3037)
at java.util.Collections$CheckedCollection.add(Collections.java:3080)
at Test.add(Test.java:13)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With