Java 泛型
泛型的好处
- 代码更健壮(编译器警告,不会出现ClassCastExceptiion);
- 代码更加简洁(不用强转);
- 代码更灵活,复用。
泛型有三种使用方式,分别为:泛型类、泛型接口、泛型方法
泛型类
1 | public class Test<T>{ |
泛型接口
1 | //定义一个泛型接口 |
当实现泛型接口的类,未传入泛型实参时
1 |
|
当实现泛型接口的类,传入泛型实参时:
1 | class Student implements Person<String>{ |
泛型方法
1 | /** |
1 | public <T> T get(Class<T> tClass){ |
泛型擦除
泛型信息只存在于代码编译阶段,在进入 JVM 之前,与泛型相关的信息会被擦除掉,专业术语叫做类型擦除。
1 | List<String> l1 = new ArrayList<String>(); |
1 | puclic interface Test<T>{ |
我们现在可以下结论了,在泛型类被类型擦除的时候,之前泛型类中的类型参数部分如果没有指定上限,如
//泛型擦除在编译期间,其实常量池里面保留了泛型信息,所哟我们可以通过反射获取泛型信息。常见retrofit中获取泛型类型传入的JavaBean 对象
Java泛型原理?什么是泛型擦除机制?
Java 的泛型是JDK5引入的特性,为了向下兼容,虚拟机其实不支持泛型,所以Java实现的是一种违泛型机制,也就是说Java在编译期擦除了所有的泛型信息,这样Java就不需要产生新的类型到字节码,所有的泛型类型最终都是一种原始类型,在java运行时根本就不存在泛型信息。
Java编译器具体是如何擦除泛型的?
- 检查泛型类型,获取目标类型;
- 擦除类型变量,并替换为限定类型 ;如果泛型类型的类型没有变量设定
,则用>Object作为原始类型,如果限定(T exends Xclass)则用Xclass作为原始类型;如果有多个限定(T extends XClass &XClass2)则使用第一个边界XClas作为原始类。 - 在必要时插入类型转换以保持类型安全;
- 生成桥方法以在扩展时保持多态性。
使用泛型以及泛型擦除带来的影响(副作用)
- 泛型类型不能使用基本类型 ;(类型ArrayList
泛型擦除后就是 ArrayList - 不能使用instanceof 运算符(类型ArrayList
泛型擦除后就是 ArrayList - 泛型在静态方法静态类中的问题(因为泛型类中的泛型参数的实例化是定义在泛型类对象(比如ArrayList
的时候指定的,二静态成员是不需要使用对象来调用的,所以对象都没有创建,如何确定在这个泛型参数是什么。)) - 泛型类型中的方法冲突(因为擦除后两个方法变成一样了)
- 没法创建泛型实例
- 没有泛型数组(因为数字组是协变的) T [] arry =new T[3] //不允许
通配符类型
? extends X 表示类型的上界,类型参数是X的子类
? super X 表示类型的下界,类型参数是X的超类(父类或者父类的父类)
? extends X
set方法是不允许被调用的,会出现编译错误
get方法则没问题,会返回一个X类型的值。
道理很简单,? extends X 表示类型的上界,类型参数是X的子类,那么可以肯定的说,get方法返回的一定是个X(不管是X或者X的子类)编译器是可以确定知道的。但是set方法只知道传入的是个X,至于具体是X的那个子类,不知道。
总结:主要用于安全地访问数据,可以访问X及其子类型,并且不能写入非null的数据。
? super X
表示传递给方法的参数,必须是X的超类(父类或者父类的父类 包括X本身)
但是对泛型类GenericType来说,如果其中提供了get和set类型参数变量的方法的话,set方法可以被调用的,且能传入的参数只能是X或者X的子类;
get方法只会返回一个Object类型的值。
? super X 表示类型的下界,类型参数是X的超类(包括X本身),那么可以肯定的说,get方法返回的一定是个X的超类,那么到底是哪个超类?不知道,但是可以肯定的说,Object一定是它的超类,所以get方法返回Object。编译器是可以确定知道的。对于set方法来说,编译器不知道它需要的确切类型,但是X和X的子类可以安全的转型为X。
总结:主要用于安全地写入数据,可以写入X及其子类型。
无限定的通配符 ?
表示对类型没有什么限制,可以把?看成所有类型的父类,如Pair< ?>;
比如:
ArrayListal=new ArrayList (); 指定集合元素只能是T类型
ArrayList> al=new ArrayList>();集合元素可以是任意类型,这种没有意义,一般是方法中,只是为了说明用法。
在使用上:
? getFirst() : 返回值只能赋给 Object,;
void setFirst(?) : setFirst 方法不能被调用, 甚至不能用 Object 调用;