Java泛型
Posted Steven
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java泛型相关的知识,希望对你有一定的参考价值。
一、泛型概念
泛型是JavaSE1.5的新特效,泛型的本职是参数化类型,就是说所操作的数据类型被指定为一个参数,这种参数可以用在类、接口和方法中创建,分别称为泛型类、泛型接口、泛型方法。引用泛型的好处是安全简单。
泛型机制将类型转换时的类型检查从运行时提前到了编译时,使用泛型编写的代码比使用object时强制类型转换的机制具有更好的可读性和安全性。
二、为何引入泛型
JDK5以前,对象保存到集合中,取出需进行类型的强制转换,这样不可避免就会引发程序的一些安全性问题,例如:
出现上面错误是因为list默认的类型为Object类型,在之后的循环中,由于之前在list中也加入了Integer类型的值或其他编码原因。编译阶段正常,而运行时会出现“java.lang.ClassCastException”异常。因此,导致此类错误编码过程中不易发现。
使用泛型
编译器会在编译时报错,集合内只能存储java.lang.String类型的实例
三、泛型作用
泛型是提供给javac编译器使用的,它用于限定集合的输入类型,让编译器在源代码级别上,即挡住向集合中插入非法数据。但编译器编译完带有泛形的java程序后,生成的class文件中将不再带有泛形信息,以此使程序运行效率不受到影响,这个过程称之为“擦除”。例如:
使用泛型时需要注意的问题:
1.参数化类型不考虑类型参数继承关系:
List<String> list = new ArrayList<Object>(); //error
List<Object> list = new ArrayList<String>(); //error
2.使用泛形时,泛形类型须为引用类型,不能是基本数据类型
3.在创建数组实例时,数组元素不能是参数化类型
四、泛型的通配符扩展应用
4.1 通配符
问题:定义一个方法,接收一个任意集合,并打印出集合中的所有元素,如下所示:
l.由于print方法c参数的类型为Collection<?>是一种不确定的类型,因此在方法体内不能调用与类型相关的方法,例如add()方法。
2.总结:使用?通配符主要用于引用对象,使用了?通配符,就只能调对象与类型无关的方法,不能调用对象与类型有关的方法
4.2 限制的通配符
a. 限定通配符的上边界:(?必须是Number的子类)
b.限定通配符的下边界 : (?必须是Integer的父类)
4.3 自定义泛型方法
泛型方法定义规则:
Java程序中的普通方法、构造方法和静态方法中都可以使用泛型。方法使用泛形前,必须对泛形进行声明,语法:<T>,T可以是任意字母,但通常必须要大写。<T>通常需放在方法的返回值声明之前。
泛型方法定义:
public static <T> T marshalle(T arg){}
泛型方法定义注意问题:
1.只有对象类型才能作为泛型方法的实际参数
2.在泛型中可以同时有多个类型
五.自定义泛型类
如果一个类多处都要用到同一个泛型,这时可以把泛形定义在类上(即类级别的泛型)
语法格式如下:
加上限定符,就可以访问限定类型的方法,类型更明确
注:我们知道final类不可继承,在继承机制上class SomeString extends String
是错误的,但泛型限定符使用时是可以的:<T extends String>
,只是会给一个警告。
后面的通配符限定有一个super关键字,这里没有。
六、泛型擦除
泛型只在编译阶段有效,编译后类型被擦除了,也就是说jvm中没有泛型对象,只有普通对象。所以完全可以把代码编译为jdk1.0可以运行的字节码。
擦除的方式:
1.定义部分,即尖括号中间的部分直接擦除
public class GenericClass<T extends Comparable>{} 擦除后: public class GenericClass{} 2.引用部分如: public T field1; 其中的T被替换成对应的限定类型,擦除后: public Comparable field1; 3.如果没有限定类型: public class GenericClass<T>{ public T field1; } 那么的替换为object,即: public class GenericClass{ public Object field1; } 4.有多个限定符的,替换为第一个限定类型名。如果引用了第二个限定符的类对象,编译器会在必要的时候进行强制类型转换 public class GenericClass<T extends Comparable & Serializable>{ public T field1; } 类擦除后变为: public class GenericClass{ public Comparable field1; } 而表达式返回值返回时,泛型的编译器自动插入强制类型转换。
七、泛型的约束和限制
不能使用8个基本类型实例化类型参数
原因在于类型擦除,Object不能存储基本类型:byte,char,short,int,long,float,double,boolean
从包装类角度来看三个: Number(byte,short,int,long,float,double),char,boolean
类型检查不可使用泛型: if(aaa instanceof Pair<String>){}//error Pair<String> p = (Pair<String>) a;//warn Pair<String> p; Pair<Integer> i; i.getClass()==p.getClass();//true
不能创建泛型对象数组 GenericMethod<User>[] o=null;//ok o=new GenericMethod<User>[10];//error 可以定义泛型类对象的数组变量,不能创建及初始化。 注,可以创建通配类型数组,然后进行强制类型转换。不过这是类型不安全的。 o=(GenericMethod<User>[]) new GenericMethod<?>[10]; 不可以创建的原因是:因为类型擦除的原因无法在为元素赋值时类型检查,因此jdk强制不允许。 有一个特例是方法的可变参数,虽然本质上是数组,却可以使用泛型。 安全的方法是使用List。
注:
1. 泛型类中,<T>称为类型变量,实际上就相当于在类中隐形的定义了一个不可见的成员变量:`private T t;`,这是对象级别的,对于泛型类型变量来说是在对象初始化时才知道其具体类型的。而在静态域中,不需要对象初始化就可以调用,这是矛盾的。
2. 静态的泛型方法,是在方法层面定义的,就是说在调用方法时,T所指的具体类型已经明确了。
以上是关于Java泛型的主要内容,如果未能解决你的问题,请参考以下文章
什么意思 在HashMap之前 ? Java中的泛型[重复]