你可能知道一个无界的通配符数组如Set<?>
能容纳任何类型的元素,同样一个原始的Set也可以。那它们之间有什么不同呢?
关于Set<?>
的两个事实
这是两个关于Set<?>
的事实:
- 第一条:因为是用了?来代表任何类型,所以
Set<?>
可以容纳任何类型的元素。
- 第二条: 因为我们不知道?是什么类型,所以我们不能把任何类型放入
Set<?>
。
所以Set<?>
可以容纳任何类型的元素(满足第一条)。但是我们不能放入任何元素(满足第二条)。这两个条件冲突了吗?当然没有。下面用两个例子来说明:
第一条意味着这样的代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| 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 就可以进行迭代。
第二条意味着如下情况:
1 2 3 4 5 6 7
| public static void printSet(Set<?> s) { s.add(10); for (Object o : s) { System.out.println(o); } }
|
因为我们不知道<?> 类型到底是什么,所以我们不能添加别的元素,除非是null。同样的原因,我们不能使用Set<?>
进行实例化。代码如下:
1 2
| Set<?> set = new HashSet<?>();
|
Set vs. Set<?>
原始类型的Set和无界的通配符的Set<?>
到底有什么不同呢?
这样的使用是没问题的:
1 2 3 4 5 6
| public static void printSet(Set s) { s.add("2"); for (Object o : s) { System.out.println(o); } }
|
因为原始类型是没有限制的。然而,这将容易损坏集合的不变性。
简言之,通配符类型是安全的而原始类却不是。我们不能把任何元素到放入Set<?>
中。
什么时候Set<?>
比较有用?
当你使用泛型,但是你不知道时间的参数是什么。你可以使用<?>
。它只被用作方法的参数。
例如:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| ublic 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; }
|
参考文档
- Set vs. Set<?>
- Bloch, Joshua. Effective java. Addison-Wesley Professional, 2008.