1,泛型的定义
泛型也成为“参数化类型”,我们可以理解为在定义的时候由具体的类型参数化,类
似与函数的变量参数,调用时传入具体的参数来调用。
泛型的本质是为了参数化类型,也就是在不创建新的类型情况下,通过泛型指定不同
的类型的形参具体控制限制的类型。
2,泛型的引出
在学习栈的时候我们常用的是int类型定义的栈,只能存放整数类型,但实际中我们需
要将各种类型的数据放入栈中,所以我们想到了通用栈
class ObjectStack{
private Object elem[];
private int top;
public ObjectStack(){
this(10);
}
public ObjectStack(int size){
this.elem = new Object[size];
this.top = 0;
}
public void push(Object val){
this.elem[this.top++] = val;
}
public void pop(){
--this.top;
}
public Object gettop(){
return this.elem[this.top-1];
}
}
public static void main(String[] args) {
ObjectStack s1 = new ObjectStack(10);
s1.push(10);
s1.push(20);
s1.push("hello");
double data = (double)s1.gettop();//必须强转为double类型
}
注意到我们获取栈顶的时候必须要强转类型才能输出,不然会报错,为了解决这样的
问题(在编译期间),于是我们应用出了泛型。
3.泛型的使用
首先,我们定义一个泛型栈
class GenericStack<T>{
private T elem[];
private int top;
public GenericStack(){
this(10);
}
public GenericStack(int size){
this.elem = (T[])new Object[size];
this.top = 0;
}
public void push(T val){
this.elem[this.top++] = val;
}
public void pop(){
--this.top;
}
public T gettop(){
return this.elem[this.top-1];
}
}
GenericStack<Integer> s1 = new GenericStack<Integer>(10);
s1.push(10);
s1.push(20);
s1.push("hello");//报错
放我们想要将hello装进栈的时候会报错,不会再有通用栈需要强转的问题。
那到底是怎么实现的?这就涉及到了泛型的意义:
(1)在编译期间就对类型进行检查。
(2)<T>是一个占位符,当我们输入具体类型,会进行自动类型转换(如我们定义的
Integer)
4,深究泛型
我们进一步对泛型进行研究
GenericStack<Integer> s1 = new GenericStack<Integer>(10);
System.out.println(s1);
GenericStack<String> s2 = new GenericStack<String>(10);
System.out.println(s2);
System.out.println(s1.getClass()==s2.getClass());
输出观察
我们可以看出<>中的类型对类的属性根本没有影响。这就说到了泛型的擦除机制。
擦除机制:首先我们说过泛型在编译期间就判断变量类型,然后运行时会向上擦除变
量---》往基类的方向,最终都会被擦除为Object类型。
这个时候就有一个问题,我不想擦除到Object类型怎么办???
我们举例说明,找最大值
class GenericAlg<T>{//擦除到Comparable位置 Object没有comparTo接口
public T findMaxVal(T[]array){
T maxval = array[0];
for(int i = 0;i< array.length;i++){
if(maxval.compareTo(array[i])<0){//两个对象比较用compareTo方法,报错
maxval = array[i];
}
}
return maxval;
}
}
因为是两个对象比较,所以我们要用compareTo方法来比较,可是我们发现报错了,
这是因为Object类型没用compareTo方法,这就说到我们泛型的上界,我们不想让它
擦除到Object类型,我们就要找到一个上界可以调用compareTo方法的上界。
class GenericAlg<T extends Comparable<T>>{//擦除到Comparable位置
要注意的是泛型只有上界,没有下界。
这就还有个问题,如果我想通过类名直接调用fingMaxVal()方法???
简单我们可以定义static类型的方法,但是问题又来了,static方法不依赖于参数类型
与对象,我们想要返回泛型类型会报错,这个时候我们就要把static默认为泛型类型
class GenericAlg2<T>{
public static<T extends Comparable<T>> T findMaxVal(T array[]){
T maxval = array[0];
for(int i = 0;i< array.length;i++){
if(maxval.compareTo(array[i])<0){//两个对象比较用compareTo方法
maxval = array[i];
}
}
return maxval;
}
}
5,泛型中的坑
泛型中的坑总是让人防不胜防,所以我们来讲解几种最常见最容易踩到的坑:
* 1.不能new泛型类型的数组 new T[];
* 2.能不能new泛型类型的对象 不能 T jet = new T(); 需要指定类型
* 3.能不能得到泛型类型的对象数组 不能 GenericStack<Integer> s3[] = new
GenericStack<Integer>(10);
* 4.简单类型不能作为泛型的参数 类型擦除机制往基类擦 简单类型没有基类
* 5.在static的方法中不能用泛型类型的参数
static 定义的方法不依赖与对象 无法指定当前类型对象