**
为什么需要泛型
**
Java的高级特性--泛型 使用特别频繁,为什么在常见类型和自定义类型后还要加入一个泛型呢,通过两段代码我们就可以知道为何我们需要泛型
实际开发中,经常有数值类型求和的需求,例如实现int类型的加法, 有时候还需要实现long类型的求和, 如果还需要double类型的求和,需要重新在重载一个输入是double类型的add方法。
所以泛型的好处就是:
适用于多种数据类型执行相同的代码
泛型中的类型在使用时指定,不需要强制类型转换
**泛型种类:
**
-
泛型类
-
泛型接口
而实现泛型接口的类,有两种实现方法
不传入泛型实参(在new出类的实例时,需要指定具体类型):
传入泛型实参(在new出类的实例时,和普通的类没区别):
-
泛型方法
在调用方法的时候指明泛型的具体类型 ,泛型方法可以在任何地方和任何场景中使用,包括普通类和泛型类。注意泛型类中定义的普通方法和泛型方法的区别。
虽然前面两个方法虽然使用了泛型,但并不是泛型方法,使用的泛型只是在其泛型类中声明过的泛型。真正的泛型方法 <> 是必不可少的。
限定类型变量
有时候,我们需要对类型变量加以约束,比如计算两个变量的大小。使用到compareTo 方法去比较,可是如何才能保证传入的两个变量一定有compareTo 方法呢,
我们可以用限定类型变量的方式去实现,将传入变量的类型限制为实现了Comarable接口的类型
<T extends Comparable>中,T表示应该绑定类型的子类型,Comparable表示绑定类型,子类型和绑定类型可以是类也可以是接口。
同时extends左右都允许有多个,如 T,V extends Comparable & Serializable 。注意限定类型中,只允许有一个类,而且如果有类,这个类必须是限定列表的第一个。
这种类的限定既可以用在泛型方法上也可以用在泛型类上。
泛型中的约束和局限性
1.不能用基本类型实例化类型参数,例如:
MyGeneric<Int> myGeneric=new MyGeneric(100); //错误代码
MyGeneric<Integer> myGeneric=new MyGeneric(100); //正确代码
2.运行时类型查询只适用于原始类型,比如不能使用以下判断,泛型不能用instanceof关键字
flag=myGeneric instanceof MyGeneric<String>
3.泛型类的静态上下文中类型变量失效
static T instance; 泛型不起作用
不能在静态域或方法中引用类型变量。因为泛型是要在对象创建的时候才知道是什么类型的,而对象创建的代码执行先后顺序是static的部分,然后才是构造函数等等。所以在对象初始化之前static的部分已经执行了,如果你在静态部分引用的泛型,那么毫无疑问虚拟机根本不知道是什么东西,因为这个时候类还没有初始化。
4.不能实例化类型变量
this.data=new T(); //错误代码
5.不能创建参数化类型的数组
泛型类型的继承规则
上面代码中的 MyGeneric<Fruit> 和 MyGeneric<Apple>并没有继承关系,有继承关系的是具体的Fruit和Apple.正是因为前面所述的, MyGeneric<Fruit> 和 MyGeneric<Apple>没有任何关系,泛型引入了通配符的概念。通配符有三种使用方式:
? extends X 表示类型的上界,类型参数是X的子类
? super X 表示类型的下界,类型参数是X的超类。
无限定的通配符 ?比如:Pair< ?>
使用泛型通配符就 可以让两个泛型类有了继承关系
虚拟机是如何实现泛型的
虚拟机其实是没有泛型概念的,在虚拟机的实现上,对泛型实现了泛型擦除。
Java泛型只在程序源码中存在,在编译后的字节码文件中,就已经替换为原来的原生类型(Raw Type,也称为裸类型)了,并且在相应的地方插入了强制转型代码,因此,对于运行期的Java语言来说,ArrayList<int>与ArrayList<String>就是同一个类,所以泛型技术实际上是Java语言的一颗语法糖,Java语言中的泛型实现方法称为类型擦除,基于这种方法实现的泛型称为伪泛型。
另外,从Signature属性的出现我们还可以得出结论,擦除法所谓的擦除,仅仅是对方法的Code属性中的字节码进行擦除,实际上元数据中还是保留了泛型信息,这也是我们能通过反射手段取得参数化类型的根本依据。