[英]Casting generic type to a subtype
我有一个ArrayList <Number>,它是从文件中读取的。 但是我需要它是一个ArrayList <Integer>。 我知道所有数字都是整数,所以我想我可以做这样的事情:
ArrayList<Number> numbers = new ArrayList<>();
nubers.add( 1 );
// cast the list<Number> to a list<Integer>
ArrayList<Integer> ints = (ArrayList<Integer>)numbers;
ints.add( 2 );
但是,这会导致编译错误。 但是,如果我首先将数字强制转换为对象,则代码将运行完美(不安全警告的当然过程除外)。
ArrayList<Integer> ints = (ArrayList<Integer>)(Object)numbers;
那么,为什么编译器在有效转换时会抱怨我?
注意:我不是在寻求解决方案,而是在解释为什么强制转换无法编译的原因。
奖励问题:
感谢您的解释,但它们使我感到奇怪,为什么这行代码可以在某些环境下编译? 该行的简要概述:
将Collection <Bundleable>强制转换为Collection <? 扩展Item>,其中Item实现Bundleable。
list的类型信息在运行时不可用,即泛型不可更改。 因此,在运行时,强制转换无论如何都可以正常工作,就像这样:
ArrayList ints = (ArrayList)numbers;
别担心。 但是,如果允许,强制转换将导致一系列问题,例如您的以下修改代码:
ArrayList<Number> numbers = new ArrayList<>();
nubers.add( 1.0 );
// cast the list<Number> to a list<Integer>
ArrayList<Integer> ints = (ArrayList<Integer>)numbers;
Integer x = ints.get(0); // trouble here
基本上,您在List<Number>
添加了一个double
类型,这非常好。 如果编译器允许强制转换,那么它也将在运行时传递。
更进一步,最后一行(带注释的一行)将因ClassCastException
而失败,因为您只是试图将Double
类型分配给Integer
类型。 这就是编译器不允许执行此类强制转换的原因。 而且,在泛型的情况下,您不应忽略编译器未检查的强制转换警告(即使这是警告)。
考虑以下声明:
ArrayList<Number> numbers;
以后可以使用扩展Number
类的任何泛型实例化ArrayList
numbers
,例如:
numbers = new ArrayList<Float>();
现在,您可以看到以下强制转换的问题:
ArrayList<Integer> ints = (ArrayList<Integer>)numbers;
但是numbers
包含Float
,而不是Integer
。 顺便说一句,将Object
强制转换为确实可以编译,但是会发出警告:
ArrayList<Integer> ints = (ArrayList<Integer>)(Object)numbers;
Note: Test.java uses unchecked or unsafe operations.
Note: Recompile with -Xlint:unchecked for details.
编译器抱怨未经检查的转换,因为它正确地推断出泛型类型可能不匹配。
请记住,泛型不是实类型,它们有类型擦除。 因此,编译器在编译为字节码之前会尝试验证有关泛型使用方式的许多事情。 由于ArrayList<Integer>
不是ArrayList<Number>
子类型(即使: Integer
是Number
子类型,或者Number
的列表中充满了Integer
),因此编译器会抱怨直接转换,因为这样做可能太危险了(并非真正相关的类型)。 在第二种情况下,您以某种方式跳过了编译器的类型检查,因为当然您的每种类型都是Object
子类型,并且编译器无法强制执行规则,但是他可以警告您有关失败的信息...
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.