【问题标题】:Casting to generic variable转换为泛型变量
【发布时间】:2016-07-19 07:30:43
【问题描述】:

我了解泛型和转换,但我不了解泛型转换。我以为我只能通过继承树向上或向下转换为特定类型,但这证明我错了:

ArrayList<?> cislo = (ArrayList<? extends Number>) new ArrayList<Integer>();

这可能不是最好的例子,但希望你能明白我的意思。这是如何运作的?它被转换成什么类型​​?

【问题讨论】:

  • 在运行时根本没有强制转换,因为通用信息丢失(类型擦除)。在编译时,您告诉编译器将 ArrayList&lt;Integer&gt; 处理为 ArrayList&lt;? extends Number&gt; 并保持安静(除了警告,基本上它与将 Number 变量转换为 Integer 相同 - 你告诉你知道你在做什么的编译器)。对ArrayList&lt;?&gt; 的分配在任何情况下都有效。
  • 使用泛型,根本不需要强制转换。如果您想转换一个 Generic 类型,也许您应该使用泛型具有的巨大灵活性来检查是否没有不进行转换的“干净”解决方案。如果你有一个具体的例子,我相信很多人都愿意提供帮助。
  • 我确实有具体的例子。我使用允许注释 JUnit 测试以选择存储的框架我想将结果发送到例如 @ReesmoConfiguration(storage = RestApiStorage.class) 并且在抽象超类 Storage 中我找到了工厂方法 newInstance(Object configuration){ Class&lt;? extends Storage&gt; clazz = null; if (Bool.FALSE.equals(Property.ENABLED.get(configuration))) { clazz = DummyStorage.class; } else { clazz = (Class&lt;? extends Storage&gt;) Property.STORAGE.get(configuration); } if (clazz.isAssignableFrom(DummyStorage.class)) { return new DummyStorage(); }}
  • 好的,这基本上是Class&lt;? extends Storage&gt; clazz = (Class&lt;? extends Storage&gt;) Property.STORAGE.get(configuration); - 如果get 返回的对象是Storage. The cast is necessary if the return type of get 的子类型的类对象,那么这将起作用@get` 是那个的超类型(比如Object或者一些接口)

标签: java generics casting


【解决方案1】:

演员表是不必要的。

任务

ArrayList<?> list = new ArrayList<Integer>();
ArrayList<? extends Number> list2 = new ArrayList<Integer>();

不需要任何显式转换。

通配符类型比具体类型“更通用”/“不太具体”,upper bounded wildcard types(如? extends Number)比unbounded ones?)更具体。

有关通配符类型之间关系的更多信息,请参见Oracle Tutorial on Wildcards and Subtyping

JLS 的相关部分指定这是4.10.2. Subtyping among Class and Interface Types

给定一个泛型类型声明 C (n > 0),参数化类型 C 的直接超类型,其中 Ti (1 ≤ i ≤ n) 是一个类型,如下:

  • D,其中 D 是泛型类型,它是泛型类型 C 的直接超类型,θ 是替换 [F1:=T1,...,Fn:=Tn]。
  • C,其中 Si 包含 Ti (1 ≤ i ≤ n) (§4.5.1)。

[...]

指的是 4.5.1. Type Arguments of Parameterized Types

如果 T2 表示的类型集在自反和传递闭包下可证明是 T1 表示的类型集的子集,则称一个类型参数 T1 包含另一个类型参数 T2,写作 T2

  • ?扩展 T

  • 扩展 S
  • ?扩展 T

[...]

所以根据这个定义,ArrayList&lt;? extends Number&gt;ArrayList&lt;Integer&gt; 的超类型,ArrayList&lt;?&gt; 是除原始类型之外的任何 ArrayList&lt;&gt; 的超类型。

为超类型类型的变量赋值不需要强制转换。


如果您想分配不同类型的东西,则需要强制转换:

Object list = new ArrayList<Integer>();
//...somewhere else - the compiler does not know that list is always an ArrayList, but we tell it that we know what we are doing
List<? extends Number> numbers = (List<? extends Number>) list; //unchecked cast warning, but works

在那次演员之后,你可以例如从列表中获取元素并将它们视为Numbers。如果您为list 引用分配了其他不是List&lt;? extends Number&gt; 子类型的引用,则强制转换将在运行时失败

    Object list = new HashSet<Integer>();
    List<? extends Number> numbers = (List<? extends Number>) list; //unchecked cast warning

在运行时抛出失败

java.lang.ClassCastException: java.util.HashSet cannot be cast to java.util.List


当泛型类型不匹配时问题开始出现:

List<String> stringList = new ArrayList<String>();
stringList.add("hi");
Object list = stringList;
List<? extends Number> numbers = (List<? extends Number>) list; //unchecked cast warning, but (sadly) works

Number n = numbers.get(0); //fails: java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Number

这是因为在运行时,列表类型的擦除匹配。另见the tutorial on erasure

【讨论】:

  • "演员表是不必要的。" - 正如我所说,这不是最好的例子。我发现 jls 真的很难阅读,但你的解释真的很有帮助,谢谢。更多信息可以在这里找到stackoverflow.com/questions/2776975/…
猜你喜欢
  • 2021-10-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-06-11
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多