Java 为什么要列出<;类型>;在编译和执行时不吸收类型元素?

Java 为什么要列出<;类型>;在编译和执行时不吸收类型元素?,java,exception,generics,types,casting,Java,Exception,Generics,Types,Casting,我有这个演示,我不需要一个具有重新绘制的架构的特定解决方案,但我只需要理解为什么会有这样的行为,以及我为避免它而遗漏的任何东西。我想知道为什么: 编译器允许在列表中插入不是列表类型的元素 当我们尝试获取元素而不是推送元素时,会引发ClassCast异常 import Test.*; //Inner classes import java.util.List; import java.util.ArrayList; public class Test<E extends Object&g

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

  • 编译器允许在列表中插入不是列表类型的元素
  • 当我们尝试获取元素而不是推送元素时,会引发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); }
    }
    
    导入测试。*//内部类
    导入java.util.List;
    导入java.util.ArrayList;
    公开课考试{
    私有列表=新的ArrayList();
    公共测试(){}
    公共静态void main(字符串[]args){
    测试a=新测试();
    a、 addElement(新字符串());
    a、 addElement(新整数(0));//在comp/运行时中没有抱怨,我不明白为什么这里没有抛出CastException
    字符串class1=a.getElement(0);
    String class2=a.getElement(1);//comp中没有投诉,但ClassCastException:整数不能转换为字符串
    //整数class3=a.getElement(1);//编译错误
    }
    public void addElement(对象节点){list.add((E)节点);}//在comp/运行时中没有投诉
    公共E getElement(int-index){return list.get(index);}
    }
    

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

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

public void addElement(E node) {
  list.add(node);
}
一旦使用对象强制转换并将其强制转换为E,就失去了编译时类型检查的好处。这就是为什么我们首先使用泛型,以避免使用对象变量和强制转换


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

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

public void addElement(E node) {
  list.add(node);
}
一旦使用对象强制转换并将其强制转换为E,就失去了编译时类型检查的好处。这就是为什么我们首先使用泛型,以避免使用对象变量和强制转换

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

问题在于
(E)由于类型擦除,节点
不会执行运行时类型检查。编译后,有关泛型类型的所有信息都将丢失

Java编译器应该警告您:

Type safety: Unchecked cast from Object to E
您可以使用以下参考手动执行此检查:

私有类clazz;
公开考试(课堂){
this.clazz=clazz;
}
公共无效添加元素(对象节点){
list.add(clazz.cast(node));
}
公共静态void main(字符串[]args){
测试=新测试(String.class);
测试。附录(“测试”);
测试.加法(整数.intValue(10));
}
问题在于
(E)由于类型擦除,节点
不执行运行时类型检查。编译后,有关泛型类型的所有信息都将丢失

Java编译器应该警告您:

Type safety: Unchecked cast from Object to E
您可以使用以下参考手动执行此检查:

私有类clazz;
公开考试(课堂){
this.clazz=clazz;
}
公共无效添加元素(对象节点){
list.add(clazz.cast(node));
}
公共静态void main(字符串[]args){
测试=新测试(String.class);
测试。附录(“测试”);
测试.加法(整数.intValue(10));
}

@Pshemo:是的,你是对的。仿制药将为我们解决这个问题。谢谢你的接球!我最后说了。由于外部原因,无法触摸此行。无论如何,为什么它不应该工作,我可以传递一个对象并尝试将其转换为整数或字符串(类型E)@user1352530:一旦强制转换,就失去了编译时类型检查的好处。这就是为什么我们首先使用泛型,以避免使用
Object
变量并避免强制转换。@user1352530:在运行时,由于泛型类型擦除,列表只是一个对象列表,因此第一次强制转换甚至不会发生。但是,当您从列表中提取项目时,会发生场景转换并导致您的异常。@user1352530:最好将泛型视为编译时注释。它们实际上在运行时没有被检查。@Pshemo:是的,你是对的。仿制药将为我们解决这个问题。谢谢你的接球!我最后说了。由于外部原因,无法触摸此行。无论如何,为什么它不应该工作,我可以传递一个对象并尝试将其转换为整数或字符串(类型E)@user1352530:一旦强制转换,就失去了编译时类型检查的好处。这就是为什么我们首先使用泛型,以避免使用
Object
变量并避免强制转换。@user1352530:在运行时,由于泛型类型擦除,列表只是一个对象列表,因此第一次强制转换甚至不会发生。但是,当您从列表中提取项目时,会发生场景转换并导致您的异常。@user1352530:最好将泛型视为编译时注释。它们实际上在运行时不会被检查。由于类型擦除,对泛型类型的强制转换不会抛出
ClassCastException
。(除非类型有一个具体的上限,但它只检查该上限。)转换为泛型类型不会抛出
ClassCastException
,因为