一.What?(什么是泛型)
Java 泛型(generics)是 JDK 5 中引入的一个新特性, 泛型提供了编译时类型安全检测机制,该机制允许程序员在编译时检测到非法的类型。
泛型的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数。
二.Why?(为什么要泛型)
jdk1.5增加泛型主要是为了集合记住其元素类型,如果没有泛型的话,集合就会忘记数据的类型,把所有对象当做object类型处理,这样我们在取出来的时候,如果没有强制转换就会报ClassCastException,增加泛型之后就可以解决这种问题,我们在写代码时,如果添加了非满足类型,则会提示报错,这样会让程序更加简洁和健壮。
我们来看一段代码:
public static void main(String[] args) {
ArrayList arrayList = new ArrayList();
arrayList.add("test");
arrayList.add(1);
for (int i = 0; i < arrayList.size(); i++) {
String item = (String) arrayList.get(i);
System.out.println(item);
}
}
上面的代码就是在没有使用泛型的情况下,我们在for循环取出数据时,如果没有做对应类型的强转,就会在控制台上报如下错误:Exception in thread “main” java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.Stringat com.ts.beta.TsFanxing.main(TsFanxing.java:16),当然你也可以做对应的强转,这样就显得特臃肿。所以我们增加了泛型,在编译时就会给出提示,下面来看一段加了泛型的代码:
public static void main(String[] args) {
ArrayList<String> arrayList = new ArrayList();//指定了实际的添加数据类型为String类型
arrayList.add("test");
arrayList.add(1);//提示報錯,编译都不会通过,这样就保证了我们在添加和取出数据时,类型一致。
for (int i = 0; i < arrayList.size(); i++) {
String item = (String) arrayList.get(i);
System.out.println(item);
}
}
这样用泛型的话,是不是增强了代码的健壮性,也不用强转了。
三.How?(怎样使用泛型)
当我们了解到什么是泛型,为什么需要泛型之后,我们就来讲一讲怎么使用泛型了。java中泛型有泛型方法、泛型类,泛型接口等,还有通配符等知识。那我们就一个一个的来说吧。
1.泛型方法
泛型方法声明都有一个类型参数声明部分(由尖括号分隔,可以有多个,用逗号隔开),该类型参数声明部分在方法返回类型之前,类型参数能被用来声明返回值类型,并且能作为泛型方法得到的实际参数类型的占位符。和普通方法相比多了类型参数声明。注意类型参数只能代表引用型类型,不能是原始类型(像int,double,char的等)结构一般如下:
修饰符 <T,S>(类型参数声明,多个用逗号隔开) 返回值类型 方法名(形参列表){ }
,举个例子,比如说打印任意类型的数组// 泛型方法
public static < E > void printArray( E[] inputArray )
{
// 输出数组元素
for ( E element : inputArray ){
System.out.printf( "%s ", element );
}
System.out.println();
}
我们在声明方法时,使用泛型定义类型参数,这样就可以打印任意类型的数组了。如果不使用泛型方法,可能你需要定义适应各种参数类型的打印方法了。
2.泛型类,接口
泛型类和泛型接口的声明和普通的声明相比是在类名后加上类型参数声明部分,和泛型方法一样,如果多个参数就用逗号隔开,这些类被称为参数化的类或参数化的类型。需要注意的点是: 1.如果子类继承参数化类型的父类,则需指定类型或不要尖括号,除非子类也是参数化的类型。2.静态属性、静态方法、初始化模块中不可食用泛型形参。
3. 通配符的使用
1.类型通配符一般是使用?代替具体的类型参数,先看个例子:
public static void main(String[] args) {
Box<Number> name = new TsFanxing.Box<Number>(99);
Box<Integer> age = new TsFanxing.Box<Integer>(712);
printData(name);
// printData(age); // 虽然Integer 是Number子类,但在泛型中并不是 所以在编译时提示报错
// The method printData(TsFanxing.Box<Number>) in the type TsFanxing
// is not applicable for the arguments (TsFanxing.Box<Integer>)
}
public static void printData(Box<Number> data) {
System.out.println("data :" + data.getData());
}
static class Box<T> {
private T data;
public Box() {
}
public Box(T data) {
setData(data);
}
public T getData() {
return data;
}
public void setData(T data) {
this.data = data;
}
}
这个时候我们就需要用到通配符了,在getData这个方法将Number替换成类型通配符?便可解决。
2.类型通配符的限定.主要对类型实参又有进一步的限制,上限用extends关键字实现,如上面的printData可以这样用: public static void printData(Box<?exnteds Number> data) {
System.out.println(“data :” + data.getData());
}
这样就限定了实际参数必须是Number类或其子类,下限用super关键字,限定类型形参必须是其类或其父类。
本文如果不正之处。欢迎大家批评指正。