有一种方法:
public static <T> void addandDisp(Collection<T> cs, T t)
以下列方式调用:
List<? extends Object> ls2 = new LinkedList<Number>();
addandDisp(ls2,new Object());
这会产生编译时错误。另一方面,如果我们只有一个参数,则调用成功。那是为什么?
此外,这是成功的:
List<? super String> ls1 = new LinkedList<String>();
addandDisp(ls1,new String());
虽然不是这样:
List<? super String> ls1 = new LinkedList<Object>();
addandDisp(ls1,new Object());
底层逻辑是什么?
答案 0 :(得分:11)
我认为addandDisp
表示添加并显示。
当你有一个集合时,定义为:
List<? extends Object> ls2 = new LinkedList<Number>();
这意味着编译器允许您将收集分配给Object
的所有可能未知子类型。由于您具有操作add
,编译器拒绝为您提供绿灯,因为它不知道所提供的对象的类型是否满足未知子类型的限制< / strong> of Object。这称为协方差。
类似的,当你有这样的定义时:
List<? super String> ls1 = new LinkedList<String>();
编译器允许您将ls1
分配到:
LinkedList<String>(); //you can add Strings to the list
LinkedList<Object>(); //you can add Strings and Objects to the list
在这种情况下,编译器将完全了解您尝试传递的对象是否满足条件是集合的泛型类型的子类型。这称为逆变。
更多信息:
答案 1 :(得分:2)
这是另一种方法,但其他人给出了非常详细的回应。
第一个例子:
List<? extends Object> ls0 = new LinkedList<Number>();
addandDisp(ls0, new Object());
此处ls0
可以是Object
的子类的任何类型,如您的示例所示的Number
。
现在怎么办呢?这意味着如果您可以将Object
放入任何集合中。
想象一下,我想迭代你之前的Number
个对象列表:
for (Number n : numbers) {
...
}
如果我可以在这里添加Object
,那么 Plaff!我会立即得到一个类强制转换异常。
第二个例子
List<? super String> ls1 = ...;
addandDisp(ls1,new String());
让我们对对象层次结构稍微进行一下:String
是CharSequence
的子类,它是Object
的子类。
这里得到ls1
这是一个未知的字符串超类型(例如,CharSequence
)。将String
放入字符序列列表是否可以?是的,没关系,因为他们肯定共享相同的界面!你可以用同样的方式处理它们。
第三个例子
List<? super String> ls1 = ...;
addandDisp(ls1,new Object());
让我们假设前一种情况:您为ls1
分配CharSequence
个列表。您可以将对象添加到List<CharSequence>
吗?不,原因与第一个例子相同。
希望有助于澄清您的问题: - )
答案 2 :(得分:1)
另一方面,如果我们只有一个参数,那么呼叫成功。那是为什么?
您的方法只有一个类型参数。调用方法时,要么显式提供类型参数,要么隐式推断出一个类型参数。
当你像
一样调用它时addandDisp(ls2, new Object());
编译器需要从两个方法参数中提取要绑定到T
的类型,因为两个方法参数都依赖于方法的类型参数T
。
此处的问题是? extends Object
和Object
不会生成可以安全绑定到T
的单个类型。想象一下你有
public static <T> void addandDisp(Collection<T> cs, T t) {
cs.add(t);
}
...
List<? extends Object> ls2 = new LinkedList<Number>();
addandDisp(ls2, new Object());
不应允许此add
,因为Object
不应在预期使用Number
的情况下使用。类型安全会破裂。
如果你有一个参数
public static <T> void addandDisp(Collection<T> cs) {
然后编译器使用单个参数来推断类型参数。然后,对于使用什么没有任何含糊之处。
答案 3 :(得分:0)
1示例:Object
不是? extends Object
(例如,Number
是? extends Object
,但Object
不是Number
)。因此,它不是类型安全的。
2示例:String
是? super String
。它是类型安全的。
3示例:Object
不是? super String
。不安全了。