一 、为什么要使用泛型?
现在,有下面两个类:
class A{
public void a() {
//do something
}
}
class B {
public void b() {
//do something
}
}
如果我们需要一个ArrayList,用来装载A类对象,然后执行a方法,没有泛型的话我们需要这么做:
public static void main(String[] args) {
ArrayList list = new ArrayList();
list.add(new A());
list.add(new A());
list.add(new A());
list.add(new B());//语句1
for(A a: list) {
a.a();
}
}
就算我们向List加入B类对象,编译器也不会报错,只有等到程序运行的时候,我们才知道,我们加入了不该加的东西。没有泛型,我们无法确认自己加进去的是什么东西,而此时也不会报错。但是如果使用泛型,那么我们就能保证加进去的东西是我们想要的。
如果上面那段代码使用泛型:
public static void main(String[] args) {
ArrayList<A> list = new ArrayList<>();
list.add(new A());
list.add(new A());
list.add(new A());
list.add(new B());//语句1
for(A a: list) {
a.a();
}
}
如果这时候再向list添加B类的对象,那么编译器将会报错。
二 、泛型是如何工作的?
- 泛型一个重要的作用,就是保证了“类型安全”,即,当有不正确的类型加入的时候,编译器会报错。
- 泛型另一个重要的点,就是类型擦除:使用了泛型的代码的字节码,和没有使用泛型的字节码是一样的。
举个例子来解释一下:
List<Integer> list = new ArrayList<Integer>();
list.add(1000); //works fine
//list.add("lokesh"); //语句1
类型安全,使得不是Integer的对象无法被加入。
List list = new ArrayList();
list.add(1000);
类型擦除,让这段代码的字节码和上面那段代码的字节码一样。泛型其实只是一个语法糖,它保证了类型安全,但是编译器在编译的时候,会把有关泛型的信息消除。
三、泛型的具体应用
1. 泛型类,接口
如果没有泛型,我们要实现一个可以根据不同场景加入不同对象的类,我们只能这样实现:
public class GenericDemo {
Object obj;
public Object get() {
return obj;
}
public void set(Obj obj) {
this.obj = obj;
}
}
我们在get的时候,还得把Object转换为我们需要的类型,非常麻烦,且不安全。
有了泛型以后:
public class GenericDemo<T> {
T obj;
public T get() {
return obj;
}
public void set(T obj) {
this.obj = obj;
}
}
这样,我们就能任意指定自己所需要的类型。而且,泛型类,接口可以同时定义多个泛型,如下:
//Generic interface definition
interface DemoInterface<T1, T2>
{
T2 doSomeOperation(T1 t);
T1 doReverseOperation(T2 t);
}
//A class implementing generic interface
class DemoClass implements DemoInterface<String, Integer>
{
public Integer doSomeOperation(String t)
{
//some code
}
public String doReverseOperation(Integer t)
{
//some code
}
}
2. 泛型方法
现在有这么一个需求,判断一个list中有多少个和item相同的对象,但是item的类型不确定,那么要怎么做?这时候就可以使用泛型方法来解决。
public static <T> int countAllOccurrences(T[] list, T item) {
int count = 0;
if (item == null) {
for ( T listItem : list )
if (listItem == null)
count++;
}
else {
for ( T listItem : list )
if (item.equals(listItem))
count++;
}
return count;
}
四、泛型通配符
泛型通配符的主要使用方式有三种:
Collection<?>
List<? extends Number>
Comparator<? super String>
第一种,表示Collection的泛型类型可以是任何类。
第二种,表示List的泛型类型必须是Number
的子类。
第三种,表示Comparator的泛型类型必须是String
的父类。
例如,下面这三条语句,就是泛型通配符的正确使用方式:
Collection<?> coll = new ArrayList<String>();
//OR
List<? extends Number> list = new ArrayList<Long>();
//OR
Pair<String,?> pair = new Pair<String,Integer>();
而下面这两条语句,将会报错:
List<? extends Number> list = new ArrayList<String>(); //String is not subclass of Number; so error
//OR
Comparator<? super String> cmp = new RuleBasedCollator(new Integer(100)); //Integer is not superclass of String
五、什么情况下无法使用泛型
- 不能有static的泛型成员变量:
public class GenericDemo<T> {
public static T t; //error
}
static变量是类成员变量,所有的对象共享。如果两个对象的具体类型不一样,那么这个static成员变量到底属于哪种?因此不能有static的泛型成员变量。
- 我们无法创建泛型实例对象:
public class GenericsExample<T>
{
public GenericsExample(){
new T();//error
}
}
- 不能创建泛型的Exception