一、泛型的作用与定义
Q1、什么是泛型?
A1:泛型,即“参数化类型”。即将类型由原来的具体的类型参数化,在使用或调用时传入具体的类型。
方法中的形参 | 对标 | 泛型 |
方法中的实参 | 对标 | 具体的类型 |
Q2、为什么要使用泛型?
A2:本质是为了参数化类型。即在不创建新的类型的情况下,通过泛型指定的不同类型来控制形参具体限制的类型。
例子:
List arrayList = new ArrayList();
arrayList.add("aaaa");
arrayList.add(100);
for(int i = 0; i< arrayList.size();i++){
String item = (String)arrayList.get(i);
Log.d("泛型测试","item = " + item);
}
//该程序一定会崩溃,因为arrayList中存放了两种类型的数据,而输出时被赋给了一个字符串类型。
解决上述程序崩溃的问题,即可使用泛型。
List<String> arrayList = new ArrayList()<String>;
arrayList.add("aaaa");
arrayList.add(100); //在编译阶段,编译到这一行时编译器就会报错。
for(int i = 0; i< arrayList.size();i++){
String item = (String)arrayList.get(i);
Log.d("泛型测试","item = " + item);
}
1、泛型的特性:泛型只在编译阶段有效。
@1:在编译之后程序会采取去泛型化的措施。
@2:在编译检验泛型结果后,会将泛型的相关信息擦出,并且在对象进入和离开方法的边界处添加类型检查和类型转换的方法。
@3:泛型不会进入到运行时阶段。
@4:泛型类型在逻辑上看成是多个不同的类型,实际上都是相同的基本类型。
2、泛型的使用:泛型类、泛型接口、泛型方法。
@1:泛型类:
#1、泛型的类型参数只能是类类型,不能是简单类型。
#2、不能对确切的泛型类型使用instanceof操作。
@2:泛型接口:
#1、当实现接口的类,未传入泛型实参时,与泛型类的定义相同,在声明类的时候,需将泛型的声明一起加到类中。
//如以下代码编译器会报错
class Cat implements Animal<T>{
@override
public T printText(){
...
}
}
//正确写法应该为以下写法,即在实现泛型接口的类中声明泛型。
class Cat<T> implements Animal<T>{
@override
public T printText(){
...
}
}
#2:当实现接口的类,传入泛型实参时,所有使用泛型的地方都要替换为传入的实参类型。
@3:泛型方法:
#1、在调用方法的时候指明泛型的具体类型。
#2、形如如下形式的方法才是泛型方法。
// public与返回值中间<T>,为声明此方法为泛型方法。
public <T> T genericMethod(Class<T> tClass)throws InstantiationException ,
IllegalAccessException{
T instance = tClass.newInstance();
return instance;
}
#3、静态方法与泛型。静态方法无法访问类上定义的泛型;如果静态方法操作的引用数据类型不确定的时候,必须要将泛型定义在方法上。
#4、尽可能使用泛型方法。
@4:泛型通配符:
#1、由于同一种泛型可以对应多个版本(参数类型不确定),不同版本的泛型类实例是不可兼容的。
#2、使用通配符就可以解决上述问题。
//输出方法
public void showKeyValue1(Animal<Number> obj){
Log.d("泛型测试:", obj.getKey());
}
//new 一个对象
Animal<Integer> aInteger = new Animal<Integer>(123);
Animal<Number> aNumber = new Animal<Number>(456);
showKeyValue(aInteger);
//此时showKeyValue这个方法编译器会报错
//若使用通配符,则可以解决
public void showKeyValue1(Animal<?> obj){
Log.d("泛型测试:", obj.getKey());
}
//此时在调用showKeyValue(aInteger)方法编译不会出错
//通配符是类型实参,与Number、String、Integer一样是实际的类型。
//通配符是所有类型的父类
二、泛型的协变与逆变
1、协变和逆变用来描述类型转换后的继承关系。
2、协变
利用通配符实现泛型的协变,即向上转型。
不能add,只能get。
// 利用通配符,实现向上转型
// <? extends Person>称为子类通配符
// 实现了协变时,只能在 list 中 get ,而不能 add 。
List<? extends Person> list = new ArrayList<man>();
3、逆变
向下转型。
不能get,只能add。
// 实现向下转型
// <? super man> 为超类通配符,代表一个具体的类型。
List<? super man> list = new ArrayList<Person>();
三、PECS法则:泛型上下边界
1、PECS: producer-extends, consumer-super.
1、泛型的上下边界添加,必须与泛型的声明在一起。