将泛型类型转换为子类型

时间:2015-03-30 05:32:22

标签: java generics casting

我有一个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 );

但是,这会导致编译错误。但是,如果我首先将数字转换为Object,则代码运行完美(除了当然不安全的强制转换警告)。

ArrayList<Integer> ints = (ArrayList<Integer>)(Object)numbers;

那么为什么编译器在我的演员阵容中抱怨它是有效演员?

注意:我不是要求解决方案,而是要解释为什么演员阵容不能编译。

奖金问题:

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

集合&lt; Bundleable&gt;正在被收集到收藏&lt;?扩展项目&gt;其中Item实现了Bundleable。

3 个答案:

答案 0 :(得分:4)

列表的类型信息在运行时不可用,即泛型不可恢复。所以在运行时,演员阵容无论如何都会起作用,就像这样:

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类型。这就是编译器不允许进行此类强制转换的原因。在泛型的情况下,你不应该忽略编译器的未经检查的强制警告(即使这是警告)。

答案 1 :(得分:1)

考虑以下声明:

ArrayList<Number> numbers;

ArrayList numbers稍后可以使用扩展Number类的任何泛型进行实例化,例如:

numbers = new ArrayList<Float>();

现在,您可以通过以下演员看到问题:

ArrayList<Integer> ints = (ArrayList<Integer>)numbers;

numbers包含Float s,而非Integer s。顺便说一下,将转换为Object 编译,但会发出警告:

ArrayList<Integer> ints = (ArrayList<Integer>)(Object)numbers;

Note: Test.java uses unchecked or unsafe operations.
Note: Recompile with -Xlint:unchecked for details.

编译器抱怨未经检查的强制转换,因为它正确地推断出泛型类型可能不匹配。

答案 2 :(得分:0)

请记住,泛型不是真正的类型,它们有类型擦除。因此编译器尝试在编译为字节码之前验证有关使用泛型的方式的许多内容。由于ArrayList<Integer>不是ArrayList<Number>的子类型(即使:IntegerNumber的子类型,或者Number的列表已满Integer s),编译器抱怨直接强制转换,因为它可能太危险(不是真正相关的类型)。 在第二种情况下,您以某种方式传递编译器类型检查,因为当然每个类型都是Object的子类型,并且编译器不能强制执行规则,但他可以警告您关于垂头丧气的情况...