泛型方法类型参数推断问题
我无法理解为什么下面的第二次调用会导致编译器错误。为什么不能像第一次调用那样将 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 对此行为有任何规定,请务必包含链接。
回答
泛型是不变的,数组是协变的:
- An
Integer[]可以充当 aNumber[](因为Integer是 的子类Number) - An
ArrayList<Integer>不能充当 anArrayList<Number>(因为您可以将例如 a 添加Double到后者,但您不能将 a 添加Double到Integers列表中)。
当然,您可以尝试将 aDouble放入 a Number[],但ArrayStoreException如果您Number[]的Integer[]. 这将是运行时故障。
在语言支持泛型之前,让数组协变对于拥有某种泛型的集合是一种胡说八道。人们意识到这是一个问题(因为运行时失败很糟糕),因此为什么泛型被设计为不变的。
由于您要求提供 JLS 链接:
- Sec 4.10.2 类和接口类型之间的子类型
- Sec 4.10.3 数组类型之间的子类型化
如果您向列表类型添加了上限,您的两个示例都将编译:
<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>。
然后您无法Collection在该方法中添加任何内容(文字除外null;或者通过原始类型破坏类型安全)。