什么是泛型
泛型就是类型参数化,就是一种模板,然后在代码中为用到的类创建对应的类型。由编译器针对类型作检查。
泛型实现了编写一次,万能匹配,又通过编译器保证了类型安全,这就是泛型。
定义泛型类的一些注意事项
- 注意泛型的继承关系:可以把ArrayList向上转型为List(T不能变!),但不能把ArrayList向上转型为ArrayList(T不能变成父类)。
- 不能new泛型类型的数组 T[] t = new T[];
泛型,先检查,后编译。当检查的时候,不知道这个T是谁,编译的时候,类型擦除成Object。 - 编写泛型类,要注意泛型类型不能用于静态方法,对于静态方法,可以单独改写成“泛型”方法,只需要使用另一个类型即可。这样才能清楚地将静态方法的泛型类型和实例类型的泛型类型区分开。
- 泛型还可以定义多种类型。例如,我们希望类不总是存储两个类型一样的对象,就可以使用类型<T, K>;使用的时候,需要指出两种类型。
- 泛型方法要防止重复定义方法,例如:public boolean equals(T obj);
泛型的使用
- 使用泛型时,把泛型参数替换为需要的class类型。
- 可以省略编译器能自动推断出的类型。
- 可以在接口中定义泛型类型,实现此接口的类必须实现正确的泛型类型。
例如
class Student implements Comparable<Student>{}
- 通配符的使用
引用类型比较大小要用两个接口中的一个
class Algorithm<T extends Comparable<T>> {
public T findMax(T[] array) {
T max = array[0];
for (int i = 1; i < array.length; i++) {
//max < array[i]
if(max.compareTo(array[i]) < 0) {
max = array[i];
}
}
return max;
}
}
- 泛型有上界,但是没有下界。通配符是有上下界的(extends super)。
- 泛型适用于写东西,通配符适用于读东西,所以,一般通配符存在的地方大多是源码当中。
class Stack<T>{
public T[] objects;
public int top;
public Stack(){
// 不能 new 泛型类型的数组
this.objects = (T[]) new Object[10];
}
public void push(T obj){
objects[this.top++] = obj;
}
public T get(){
return objects[this.top-1];
}
}
public class TestDemo {
public static void main(String[] args) {
Stack<Integer> stack = new Stack();
stack.push(1);
stack.push(2);
int ret = stack.get();
System.out.println(ret);
System.out.println(stack);
Stack<Character> stack1 = new Stack<>();
stack1.push('a');
stack1.push('b');
int ret2= stack1.get();
System.out.println(ret2);
System.out.println(stack1);
}
}
Java泛型实现方式是擦拭法
擦拭法是指,虚拟机对泛型其实一无所知(不管是实现泛型还是使用泛型),所有的工作都是编译器做的。
Java使用擦拭法实现泛型,导致了
- 编译器把类型视为Object
- 编译器根据实现安全的强制转型
Java泛型的局限性
- 不能是基本类型,因为实际类型是Object,Object类型无法持有基本类型
- 无法取得带泛型的Class
- 无法判断带泛型的类型
- 不能实例化T类型
泛型的继承
一个类可以继承自一个泛型类,在继承了泛型类型的情况下,子类可以获取父类的泛型类型。