泛型是Java SE 1.5的新特性,泛型的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数。 在Java SE 1.5之前,没有泛型的情况的下,通过对类型Object的引用来实现参数的“任意化”,“任意化”带来的缺点是要做显式的强制类型转换,而这种转换是要求开发者对实际参数类型可以预知的情况下进行的。对于强制类型转换错误的情况,编译器可能不提示错误,在运行的时候才出现异常,这是一个安全隐患。 泛型的好处是在编译的时候检查类型安全,并且所有的强制转换都是自动和隐式的,以提高代码的重用率。
二、为什么需要泛型 2.1、编译期对数据类型进行检测首先我们看一个案例,向一个ArrayList中添加字符串,“不小心”添加了整数,如下面代码, 并没有错误:
但是执行时,会报错:“java.lang.classCastException”
因为ArrayList中维护的是一个Object数组, private transient Object[] elementData;
, 使用get()返回的是一个Object对象, 需要强制转换,但是中间混杂一个Integer数值, 导致强制转换失败。这个错误就是由于Object的任意化导致的。 如果能在编译阶段就发现数据类型有错, 那么就很方便,泛型就满足了这个要求:我将这个程序修改一下,ArrayList使用泛型:会发现编译阶段就报错了.
不使用泛型:
package com.chb.fanxing;
public class NoGen {
private Object ob;
public NoGen(Object ob) {
this.ob = ob;
}
getter setter...
private void showType() {
System.out.println("数据的实际类型是:" + ob.getClass().getName());
}
public static void main(String[] args) {
NoGen ngInt = new NoGen(88);
ngInt.showType();
int i = (int)ngInt.getOb();
System.out.println("value = " + i);
System.out.println("---------------");
NoGen ngStr = new NoGen("88");
ngStr.showType();
String str = (String)ngStr.getOb();
System.out.println("value = " + str);
}
}
使用泛型:
package com.chb.fanxing;
public class Gen {
private T ob;
public Gen(T ob) {
this.ob = ob;
}
getter setter...
private void showType() {
System.out.println("T的实际类型:"+ob.getClass().getName());
}
public static void main(String[] args) {
//定义一个Integer版本
Gen genInt = new Gen(88);
genInt.showType();
int i = genInt.getOb();//此处不用强制转换
System.out.println("value = " + i);
System.out.println("----------------------");
Gen genStr = new Gen("88");
genStr.showType();
String str = genStr.getOb();
System.out.println("value = "+str);
}
}
运行结果:
两个例子的运行结果是一致的
数据的实际类型是:java.lang.Integer
value = 88
---------------
数据的实际类型是:java.lang.String
value = 88
对比两个例子会发现:
- 使用泛型,强制转换时自动进行的:
int i = genInt.getOb();//此处不用强制转换
- 而不使用泛型,必须要进行手动强制转化
int i = (int)ngInt.getOb();
三、深入泛型
3.1 、有两个类,我们需要打印他们的成员变量
class StringDemo {
private String s;
public StringDemo (String s) {
this.s = s;
}
setter geter....
}
class DoubleDemo{
private Double d;
public DoubleDemo(Double d) {
this.d = d;
}
setter getter...
}
3.2、重构
仔细观察两个类功能基本一致,只是数据类型不一样,考虑到重构,因为Object是所有类的基类,所以可以使用Object作为成员变量,这样代码就可以通用了。重构代码如下:
class ObjectDemo{
private Object ob;
public ObjectDemo(Object ob){
this.ob = ob;
}
setter getter...
}
ObjectDemo测试:
public static void ObjectDemoTest(){
ObjectDemo strOD = new ObjectDemo("123");
ObjectDemo dOD = new ObjectDemo(new Double(23));
ObjectDemo od = new ObjectDemo(new Object());
System.out.println((String)strOD.getOb());
System.out.println((Double)dOD.getOb());
System.out.println(od.getOb());
}
运行结果:
发现上面的ObjectDemoTest()
中必须要使用强制转换,这比较麻烦,我们还必须事先知道要转换的数据类型,才能进行正确的转换,否则,会出现错误, 业务编译时没有问题,但是一运行,会出现”classCastException”。所以我们需要不用自己进行强制转换,这是泛型就尤为重要。
class GenDemo{
private T t;
public GenDemo(T t) {
this.t = t;
}
public void setT(T t) {
this.t = t;
}
public T getT() {
return t;
}
}
测试:省去了手动进行强制转换
public static void GenTest() {
GenDemo strOD = new GenDemo("123");
GenDemo dOD = new GenDemo(new Double(23));
GenDemo od = new GenDemo(new Object());
System.out.println(strOD.getT());
System.out.println(dOD.getT());
System.out.println(od.getT());
}
下面解释一下上面的泛型语法:
使用表示一个类型持有者名称, 相当于一个形参,数据的类型是有实际传入的数据的类型决定,然后T作为成员、参数、方法的返回值的类型。 T仅仅是一个名字,可以随意取的。 class GenDemo , T没有进行任何限制, 实际相当于 Object, 等同于 class GenDemo。 与Object相比,使用泛型所定义的类,在定义和声明,可以使用来制定真实的数据类型,如:
GenDemo dOD = new GenDemo(new Double(23)); 也可以不指定,那么就需要进行强制转换。
下文我们将继续java的泛型 限制泛型 多接口限制 通配符泛型