我无法理解为什么下面的第二次调用会导致编译器错误。为什么不能像第一次调用那样将 Number 推断为类型参数?
typeArgInference(new Integer[100], new ArrayList<Number>()); // Infers Number
typeArgInference(new Number[100], new ArrayList<Integer>()); // compiler error
<T> void typeArgInference(T[] a, Collection<T> c) {}
可能是我在这里遗漏了一些东西。如果 JLS 对此行为有任何规定,请提供链接。
答案 0 :(得分:7)
泛型是不变的,数组是协变的:
Integer[]
可以充当 Number[]
(因为 Integer
是 Number
的子类)ArrayList<Integer>
不能充当 ArrayList<Number>
(因为您可以向后者添加例如 Double
,但您不能将 Double
添加到Integer
的列表)。当然,您可以尝试将 Double
放入 Number[]
,但如果您的 ArrayStoreException
确实是 {{1} }.这将是运行时故障。
使数组协变是一种在语言支持泛型之前拥有某种泛型集合的胡说八道。人们意识到这是一个问题(因为运行时失败很糟糕),因此为什么泛型被设计为不变的。
由于您要求提供 JLS 链接:
如果您为列表类型添加了上限,那么您的两个示例都可以编译:
Number[]
在这两种情况下,Integer[]
都是满足类型约束的类型:
<T> void typeArgInference(T[] a, Collection<? extends T> c) {}
可以充当 Number
;而 Integer[]
是一个 Number[]
,它是一个 ArrayList<Number>
。Collection<Number>
可以充当 Collection<? extends Number>
(显然);而 Number[]
是一个 Number[]
,它是一个 ArrayList<Integer>
。此时您无法在该方法中向 Collection<Integer>
添加任何内容(文字 Collection<? extends Number>
除外;或通过原始类型破坏类型安全)。