本文采用部分意译
原文请看:http://java.sun.com/docs/books/tutorial/java/generics/index.html
--------------------
属性:Java内置
作用:是一些问题在编译期间被发现,从而提高软件的可靠性
一、介绍
介绍不使用泛型的缺点。
二、泛型类型
介绍泛型类型的声明、类型变量、类型参数和type arguments,并介绍了类型参数的命名规范。
三、带泛型的方法和构造函数
四、限定类型参数
五、子类
六、通配符
七、类型擦除
介绍类型擦除,原始类型和未审核警告。
一、介绍
在软件开发过程中,bugs被发现得越早越好。因此,我们要想办法尽量在编译时发现bugs,而不是运行时。
Java 泛型编程为我们提供了一种在编译时多发现一些bugs的方法。
在Java 集合框架中大量使用了泛型编程。
先看一个不用泛型的类Box,它提供了设置与获取object的方法:
由于object是Object类型的,你可以在object中存储任何类型。
下面是一个BoxDemo,在这里它使用Box来存取整型数据:
在这个程序中你要用Box来存取整型数据,你要告知其他程序员这一约束的方法之一是写一行注释。
但这样做,编译器不会知道,另外粗心的程序员也不会看到,他很可能写出一下代码:
这段代码可以通过编译,但运行时会出以下异常:
Exception in thread "main"
java.lang.ClassCastException:
java.lang.String cannot be cast to java.lang.Integer
at BoxDemo2.main(BoxDemo2.java:6)
如果Box类用泛型实现,这样的错误就可以在编译时发现,而不是运行时。
二、泛型类型
现在我们用泛型来实现Box类。
首先我们进行泛型类型声明:把"public class Box" 改成 "public class Box
(注意:泛型类型声明对于接口同样适用。)
T 叫做类型变量,这样声明之后,T 就可以在Box类中使用了。在这种情况下,我们也可以把 T 叫做Box类的正式类型参数。
代码如下:
要引用这样一个带泛型的类,必须使用泛型类型调用:
(为什么要叫“调用”呢? 思考下方法调用,如:add(8)。方法调用有圆括号,而泛型类型调用有尖括号,形式上有点像啦。)
一个泛型类型调用通常也叫做参数化类型。
那如何实例化这个类呢?
实例化后,你就可以使用这个类的实例方法了:
这样,当你想向add()方法传入一些与 T(本例为Integer)类型不兼容的类型,如String类型时,就会出现编译错误:
BoxDemo3.java:5: add(java.lang.Integer) in Box
cannot be applied to (java.lang.String)
integerBox.add("10");
^
1 error
泛型类型声明中使用的T并不是实际存在的一种类型,它也不是类名Box的一部分。实际上,在编译过程中,所有的泛型信息都会被去掉,这在后面的类型擦除中会介绍。
在泛型类型声明中可以使用多个符号,但各个符号不能相同。如,你可以声明Box
类型参数的命名规范:
E - Element (在Java集合框架中被大量使用)
K - Key
N - Number
T - Type
V - Value
S,U,V etc. - 2nd, 3rd, 4th types
三、带泛型的方法和构造函数
类型参数也可以被声明在方法和构造函数的签名当中,使它们成为带泛型的方法和构造函数。
这与声明泛型类型没什么不同,只不过类型的作用域不同罢了。
程序的输出是:
T: java.lang.Integer
U: java.lang.String
传入不同的类型,输出就会相应的改变。
带泛型的方法更为实际的用途可能会像下面这段代码:
为了使用这个方法,你的代码可能会是这样:
调用这个方法的完整格式是:
另一种较为简便的调用格式是:
这一特性叫做类型推断,它可以使你像调用普通方法那样调用带泛型的方法。
四、限定类型参数
有时候你可能想限制可以被传入的类型。比如一个操作数字的方法,它只想接受Number以及Number的子类的实例。这时候就可以用限定类型参数了。
声明一个限定类型参数的方法:写出类型参数名+extend(对于后面的上界是类用extend,若是接口用implement)+上界。
以上代码会出现编译错误,应为String不是Number类或Number的子类:
Box.java:21: inspect(U) in Box
be applied to (java.lang.String)
integerBox.inspect("10");
^
1 error
如果还想规定更多的实现接口,用 & 把限定类连起来:
五、子类
父类参数可以引用子类实例。这在面向对象领域叫做"is a"关系。
如:
这在泛型编程中同样适用:
现在,思考如下方法:
这个方法接受什么样的参数呢?从方法签名来看,它接受Box
那它能否接受Box
但结果却出乎意料,答案是:不能!因为 Box
想象有一个Cage
而Cage
现在你想要一个可以盛所有动物的万能笼子,有时可以关狮子,有时可以装蝴蝶。
而我却给你一个只能装狮子的大笼子或一个只能装蝴蝶的小笼子,这意味着你会失去狮子或蝴蝶,你会满意吗?
六、通配符
现在假设我就要一个笼子,它不是万能的,它只能装一种动物,但具体装狮子或者蝴蝶我还不确定,这就要使用限定通配了。
怎样声明这样一个笼子呢?
在这里,? 就是一个限定通配符,Animal是上界。
你也可以声明下界 super Animal>。
虽然Cage
Cage
七、类型擦除
当泛型类型的类被实例化时,编译器使用类型擦除技术,把所有有关类型的信息都去掉,以使编译后的程序能与泛型出现以前的Java类库或程序兼容。
比如Box
这意味着你无法知晓在运行时某个泛型类使用的是那种具体的类型。下面的操作是不可行的:
当传统代码与泛型代码混合使用时,编译器可能会给出如下警告信息:
Note: WarningDemo.java uses unchecked or unsafe operations.
Note: Recompile with -Xlint:unchecked for details.
如:
用 -Xlint:unchecked 选项重新编译代码,可得到详细信息:
WarningDemo.java:4: warning: [unchecked] unchecked conversion
found : Box
required: Box
bi = createBox();
^
1 warning
------------------------
词汇表:
泛型 Generics
泛型类型 Generic Types
类型变量 Type variables
类型参数 Type Parameters
限定类型参数 Bounded Type Parameters
子类 Subtyping
通配符 Wildcards
类型擦除 Type Erasure
泛型类型调用 generic type invocation
参数化类型 parameterized type
类型推断 type inference