Why a List<type> absorbs not-type element in compilation and execution time?

爷,独闯天下 提交于 2020-01-11 11:35:48

问题


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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!