泛型的构成
由泛型的构成引出了一个类型变量的概念。根据Java语言规范,类型变量是一种没有限制的标志符,产生于以下几种情况:
泛型类声明
泛型接口声明
泛型方法声明
泛型构造器(constructor)声明
如果一个类或接口上有一个或多个类型变量,那它就是泛型。类型变量由尖括号界定,放在类或接口名的后面:
<code>1</code>
<code>public</code> <code>interface</code> <code>List<T></code><code>extends</code> <code>Collection<T> {</code>
<code>2</code>
<code>...</code>
<code>3</code>
<code>}</code>
简单的说,类型变量扮演的角色就如同一个参数,它提供给编译器用来类型检查的信息。
非常的相似,如果方法和构造器上声明了一个或多个类型变量,它们也可以泛型化。
<code>public</code> <code>static</code> <code><t> T getFirst(List<T> list)</code>
这个方法将会接受一个List<T>类型的参数,返回一个T类型的对象。
通配符
在本文的前面的部分里已经说过了泛型类型的子类型的不相关性。但有些时候,我们希望能够像使用普通类型那样使用泛型类型:
向上造型一个泛型对象的引用
向下造型一个泛型对象的引用
例如,假设我们有很多箱子,每个箱子里都装有不同的水果,我们需要找到一种方法能够通用的处理任何一箱水果。更通俗的说法,A是B的子类型,我们需要找到一种方法能够将C<A>类型的实例赋给一个C<B>类型的声明。
为了完成这种操作,我们需要使用带有通配符的扩展声明,就像下面的例子里那样:
<code>List<Apple> apples =</code><code>new</code> <code>ArrayList<Apple>();</code>
<code>List<?</code><code>extends</code> <code>Fruit> fruits = apples;</code>
“? extends”是泛型类型的子类型相关性成为现实:Apple是Fruit的子类型,List<Apple> 是 List<? extends Fruit> 的子类型。
? super
使用 ? super 通配符一般是什么情况?让我们先看看这个:
<code>List<Fruit> fruits =</code><code>new</code> <code>ArrayList<Fruit>();</code>
<code>List<?</code><code>super</code> <code>Apple> = fruits;</code>
我们看到fruits指向的是一个装有Apple的某种超类(supertype)的List。同样的,我们不知道究竟是什么超类,但我们知道Apple和任何Apple的子类都跟它的类型兼容。既然这个未知的类型即是Apple,也是GreenApple的超类,我们就可以写入:
<code>fruits.add(</code><code>new</code> <code>Apple());</code>
<code>fruits.add(</code><code>new</code> <code>GreenApple());</code>
如果我们想往里面加入Apple的超类,编译器就会警告你:
<code>fruits.add(</code><code>new</code> <code>Fruit());</code>
<code>fruits.add(</code><code>new</code> <code>Object());</code>
因为我们不知道它是怎样的超类,所有这样的实例就不允许加入。
从这种形式的类型里获取数据又是怎么样的呢?结果表明,你只能取出Object实例:因为我们不知道超类究竟是什么,编译器唯一能保证的只是它是个Object,因为Object是任何Java类型的超类。