Java 泛型中 ? 通配符的理解
通配符 跟泛型参数有相同的思想. 可是在细节上却又并不相同.
先看看通配符能怎么用吧. 下面这段话摘自 Sun公司的高级Java开发人员 Brian Goetz对通配符的解释
对泛型类 ArrayList
而言,对于任意(引用)类型 T
,ArrayList<?>
类型是 ArrayList<T>
的超类型(类似原始类型 ArrayList
和根类型 Object
,但是这些超类型在执行类型推断方面不是很有用)。
通配符类型 List<?>
与原始类型 List
和具体类型 List<Object>
都不相同。如果说变量 x
具有 List<?>
类型,这表示存在一些 T
类型,其中 x
是 List<T>
类型,x
具有相同的结构,尽管我们不知道其元素的具体类型。这并不表示它可以具有任意内容,而是指我们并不了解内容的类型限制是什么 — 但我们知道存在某种限制。
看看下面这段代码:
HashSet<String> collection1 = new HashSet<Integer>(); //报错
HashSet<?> collection2 = new HashSet<Integer>();
collection2 = new HashSet<String>();
代码中第1和第3行都会在编译器中出现错误提示.
- 第1行是因为引用变量 collection1 已经被指定了具体的类型参数String, 可指向的对象中却是Integer.
- 第2行中, 通过用通配符确定了collection2 具有某种类型限制, 可是具体是什么限制在定义collection2的时候没有说明. 所以指向HashSet<Integer>这个对象自然也没问题.
- 第3行中, collection2虽然在第2行指向了具有具体参数类型的对象, 可由于collection2本身是被 HashSet<?> 所定义的, 因此在以后还可以指向具有不同的参数类型的对象.
HashSet<String> collection3 = new HashSet<String>();
collection3 = new HashSet<Integer>(); //报错
这两行代码说明了, 第一个例子里面第3行那种情况对于在定义时已经确定了参数类型的对象是不可行的.
通配符与 extends/super 边界限定符的搭配
边界限定符很容易理解, 这里就简单说说.
通过情况下, 通配符都会跟上面这两个边界限定符的其中一个或多个搭配使用来确定自己能接受类型的范围. 如果不搭配边界限定符的话, 上面例子中完全不使用通配符, 只用原始类型来定义引用变量效果是一样的. 而通配符中所说的对于类型是有限制的其限制方法就是使用边界限定符: 1) ? extends Type 代表通配符只能接受的Type和其下面的类型. 2) ? super Type 代表通配符只能接受的Type和其上面的类型.