问题
I have this demo, which I dont need a particular solution with a redrawn architecture, but just understanding why behaves like that and any thing I am missing for avoiding it. I am wondering why:
- Compiler allows the insertion of an element that its not the type of the list into the list
The ClassCast exception is thrown when we try to get the element instead of when pushing it
import Test.*; //Inner classes import java.util.List; import java.util.ArrayList; public class Test<E extends Object> { private List<E> list = new ArrayList<E>(); public Test() {} public static void main(String[] args) { Test<String> a = new Test<String>(); a.addElement(new String()); a.addElement(new Integer(0)); // No complain in comp/run-time, I dont understand why CastException is not thrown here String class1 = a.getElement(0); String class2 = a.getElement(1); //No complain in comp but ClassCastException: Integer cannot be cast to String //Integer class3 = a.getElement(1); //Compilation error } public void addElement (Object node) { list.add((E) node); } //No complain in comp/run-time public E getElement(int index) { return list.get(index); } }
What could be a solution? Notice the line addElement where I need to pass a SuperClass, instead of type E. This is needed for my architecture, this is just a simple mock demo. But anyway the casting to type E should act as desired and throw a CastExeption in runtime, should not?
回答1:
The problem is (E) node
doesn't do run-time type check due to type erasure. All the information about generic types is lost after compilation.
Java compiler should warn you about it:
Type safety: Unchecked cast from Object to E
You can do this check manually by using reference to java.lang.Class:
private Class<E> clazz;
public Test(Class clazz) {
this.clazz = clazz;
}
public void addElement (Object node) {
list.add(clazz.cast(node));
}
public static void main(String[] args) {
Test<String> test = new Test(String.class);
test.addElement("test");
test.addElement(Integer.intValue(10));
}
回答2:
Your addElement method is not using generics properly to allow for compile-time error catching. It should be:
public void addElement(E node) {
list.add(node);
}
Once you use Object cast and cast it to E, you lose compile-time type checking benefits. That's why we use generics in the first place, to avoid using Object variables and avoid casting.
Note that at run time the List is simply a List of Object due to generic type erasure, so the first cast, when you're placing the object into the list, doesn't even occur. But when you extract the item from the list and need to assign it to concrete type variable a behinds the scene cast does occur, and this causes your exception to be thrown.
来源:https://stackoverflow.com/questions/11594823/why-a-listtype-absorbs-not-type-element-in-compilation-and-execution-time