为什么要使用泛型
作用一
思考一个简单的问题:在java中一切都是对象,因此例如ArrayList也是一个对象。但由于必须支持ArrayList用来存各种各样的数据,因此ArrayList中存储的对象必然只能是基类Object。在这种情况下,编译时无法对其中存储的对象做任何检查,但运行时可能由于其中的对象不是认为的那种而出现运行时错误,因此这是不安全的。而泛型出现后:
new ArrayList<Dog>(); 这样的声明就表明该collection只能用于存储Dog,或Dog的子类对象,存储其他对象则导致编译错误,从而避免了运行时错误。
最简单的理解泛型的方式是,想一想类型转换的问题:
List<Apple> box = ...;
Apple apple = box.get(0);
这段代码是自解释的:box是一个对list对象的引用;list中的对象是apple。从list中取出对象时,不再需要类型转换。
因此,当我们使用泛型(其实就是在代码中加入了类型变量)时,就等于告诉编译器:请帮我做类型检查,并在必要的时候自动进行类型转换。
作用二
泛型的另外一个作用是,当我们在写方法时,传入的参数可以是任何primitive types或object,例如:public int testMethod(int x, int y){...} 。此时如果也需要支持输入参数是String的话,就要实现另外一个method,这样非常麻烦。但如果用public int testMethod(Object x, Object y){...} 这样也不好,因为完全不能检查输入参数是否合法。泛型可以解决这个问题:
public class GenericTest {
public static void main(String[] args) {
Box<String> name = new Box<String>("corn");
//如果不用泛型,而是在实现构造函数时使用输入参数为Object,以此来支持多种类型的输入参数,
//这样实例化出来的name中什么都可以存。但用泛型后,实例化出来的name只能用于String。因此即
//增加了灵活性,又没有损害安全性。
Box<Integer> age = new Box<Integer>(712);
Box<Number> number = new Box<Number>(314);
getData(name);
getData(age);
getData(number);
}
public static void getData(Box<?> data) {
System.out.println("data :" + data.getData());
}
}
class Box<T> { //这是一个泛型类。在java中我们可以定义泛型类,泛型方法和泛型接口。
private T data; //这是一个泛型类型。
public Box() {
}
public Box(T data) { //可以看到在声明构造函数的时候,使用泛型T就可以了。在使用构造函数时
//Box<String>('test'); 或Box<Integer>(5);都是合法的。
setData(data);
}
public T getData() {
return data;
}
public void setData(T data) {
this.data = data;
}
}
参考下面这个帖子:
http://stackoverflow.com/questions/2001755/using-int-as-a-type-parameter-for-java-util-dictionary
所以我们得知道java中常见的primitive types以及对应的object。其实在java中,就像类的名字总是首字母大写的规律一样,如果类型的名字是小写的,那么就是primitive types:
int -- Integer
char -- Character
boolean -- Boolean
而String原本就不是primitive type,而是一个对象,S是大写的。