翻译自: Set vs. Set<?>
你可能知道一个无界的通配符Set <?>可以容纳任何类型的元素,而一个原始类型Set也可以容纳任何类型的元素。 但是他们有什么区别呢?
1.Set<?>的两个事实
(1)由于问号? 代表任何类型,所以 Set <?>能够容纳任何类型的元素;
(2)因为我们不知道?的类型,所以我们不能把任何元素放入Set <?>中
所以一个Set <?>可以容纳任何类型的元素(Item 1),但是我们不能放入任何元素(Item 2)。 这两个说法是不是就相互冲突了? 当然,他们并不冲突。 这可以通过以下两个例子清楚地说明:
第一个例子:
//Legal Code
public static void main(String[] args) {
HashSet<Integer> s1 = new HashSet<Integer>(Arrays.asList(1, 2, 3));
printSet(s1);
HashSet<String> s2 = new HashSet<String>(Arrays.asList("a", "b", "c"));
printSet(s2);
}
public static void printSet(Set<?> s) {
for (Object o : s) {
System.out.println(o);
}
}
由于Set <?>可以容纳任何类型的元素,我们只需在循环中使用Object(因为Object是所有类的父类)。
第二种不合法的情况:
//Illegal Code
public static void printSet(Set<?> s) {
s.add(10);//this line is illegal
for (Object o : s) {
System.out.println(o);
}
}
因为我们完全不知道 ? 的类型,除了null之外,我们不能添加任何东西。 出于同样的原因,我们不能用Set <?>初始化一个集合。 下面的这个操作是非法的:
//Illegal Code
Set<?> set = new HashSet<?>(); // compile error:Cannot instantiate the type HashSet<?>
2. Set vs. Set<?>
原始类型Set和无界通配符Set <?>有什么区别?这个方法声明得很好:
public static void printSet(Set s) {
s.add("2");
for (Object o : s) {
System.out.println(o);
}
}
因为原始类型没有添加限制。 但是,这很容易破坏集合的不变性,简单的说,通配符类型是安全的,原始类型不是。 我们不能把任何元素放入Set <?>中。
3.Set<?>在什么时候用?
当你想使用一个泛型类型,但是你不知道或不关心参数是什么类型时,你可以使用<?> (参考:Bloch, Joshua. Effective java. Addison-Wesley Professional, 2008.), 它只能用作方法的参数。
例如:
public static void main(String[] args) {
HashSet<Integer> s1 = new HashSet<Integer>(Arrays.asList(1,2,3));
HashSet<Integer> s2 = new HashSet<Integer>(Arrays.asList(4,2,3));
System.out.println(getUnion(s1, s2));
}
public static int getUnion(Set<?> s1, Set<?> s2){
int count = s1.size();
for(Object o : s2){
if(!s1.contains(o)){
count++;
}
}
return count;
}