1

我有这个演示,我不需要具有重绘架构的特定解决方案,但只需了解为什么会这样以及我为避免它而遗漏的任何事情。我想知道为什么:

  • 编译器允许将不是列表类型的元素插入到列表中
  • 当我们尝试获取元素而不是推送它时抛出 ClassCast 异常

    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); }
    }
    

有什么解决办法?注意我需要传递 SuperClass 而不是类型 E 的 addElement 行。这是我的架构所需要的,这只是一个简单的模拟演示。但无论如何,类型 E 的强制转换应该按照需要运行并在运行时抛出 CastExeption,不应该吗?

4

2 回答 2

5

您的 addElement 方法未正确使用泛型以允许捕获编译时错误。它应该是:

public void addElement(E node) {
  list.add(node);
}

一旦您使用 Object cast 并将其强制转换为 E,您将失去编译时类型检查的好处。这就是我们首先使用泛型的原因,以避免使用 Object 变量并避免强制转换。

请注意,由于泛型类型擦除,在运行时列表只是一个对象列表,因此当您将对象放入列表时,甚至不会发生第一次强制转换。但是,当您从列表中提取项目并需要将其分配给具体类型变量时,场景转换确实会发生,这会导致您的异常被抛出。

于 2012-07-21T18:33:42.330 回答
4

问题是(E) node由于类型擦除而没有进行运行时类型检查。所有关于泛型类型的信息在编译后都会丢失。

Java 编译器应该警告你:

Type safety: Unchecked cast from Object to E

您可以使用对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));
}
于 2012-07-21T18:50:18.637 回答