将泛型类型转换为子类型

Casting generic type to a subtype

我有一个从文件中读出的 ArrayList。但我需要它是一个 ArrayList。我知道所有数字都是整数,所以我想我可以这样做:

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;

那么为什么编译器在我的转换是有效的转换时会抱怨?

注意:我不是在寻求解决方案,而是在寻求强制转换无法编译的原因的解释。

加分题:

感谢您的解释,但它们让我想知道为什么 this line 可以在某些环境中编译?该行的简要总结:

正在将集合 转换为集合 其中 Item 实现了 Bundleable。

列表的类型信息在运行时不可用,即泛型不可具体化。所以在运行时,演员无论如何都会工作,就像这样:

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

基本上,您将 double 类型添加到 List<Number> 中,这非常好。如果编译器允许转换,那么它也会在运行时通过。

更进一步,最后一行(注释行)将失败并显示 ClassCastException,因为您只是试图将 Double 类型分配给 Integer 类型。这就是编译器不允许进行此类转换的原因。在泛型的情况下,你不应该忽略编译器未经检查的转换警告(即使那是警告)。

考虑以下声明:

ArrayList<Number> numbers;

ArrayList numbers 稍后可以用扩展 Number class 的 any 泛型实例化,例如:

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> 的子类型(即使:IntegerNumber 的子类型,或者您的 Number 列表充满了 Integers),编译器抱怨 direct 转换,因为它可能太危险了(不是真正相关的类型)。 在第二种情况下,您以某种方式通过了编译器类型检查,因为当然您的每个类型都是 Object 的子类型并且编译器无法强制执行规则,但他可以警告您有关向下转换的信息...