我理解通用和铸造,但我不理解通用铸造。我以为我只能通过继承树向上或向下投射特定类型,但这证明我错了:
ArrayList<?> cislo = (ArrayList<? extends Number>) new ArrayList<Integer>();
这可能不是最好的例子,但希望你能明白我的意思。这是如何运作的?它的演员类型是什么类型?
答案 0 :(得分:4)
演员阵容是不必要的。
作业
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
类型参数T1被认为包含另一个类型参数T2,写为T2&lt; = T1,如果由T2表示的类型集合可证明是由T1表示的类型集合的子集,在反射和传递闭包下以下规则(其中&lt;:表示子类型(§4.10)):
- ,则扩展S.
?延伸T <=?如果T&lt ;: S
?扩展T&lt; =?
[...]
因此,通过此定义,ArrayList<? extends Number>
是ArrayList<Integer>
的超类型,ArrayList<?>
是除原始类型之外的任何ArrayList<>
的超类型。
分配给超类型的变量不需要转换。
如果要分配不同类型的内容,则必须进行强制转换:
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
在演员之后,您可以例如从列表中获取元素并将其视为Number
。如果您为list
引用指定了不属于List<? extends Number>
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