关键字:泛型、类型擦除、泛型实现、泛型缺点、泛型运用。
1.不使用泛型会怎么样?
例子1,int、String元素都可以放入list中,但在取元素的时候,需要做类型转换,如果类型不匹配,只有在运行时才知道错误,注意如果没有给List定义类型,默认是Object;
private void test() {
List list=new ArrayList();
list.add(1);
list.add("String");
int data1= (int) list.get(0);
String data1= (String) list.get(1);
}
例子2,Person类的data属性可以存放任意类型的数据,但在get的时候需要做类型转换,如果类型不匹配,只有在运行时才知道错误;
//定义
public class Person {
private Object data;
public void setData(Object data){
this.data=data;
}
public Object getData(){
return this.data;
}
}
//调用
Person person=new Person();
person.setData("name");
String name= (String) person.getData();
例子1代码使用泛型,list定义为List<Integer>,只能存放Integer、int类型元素,如果放入其他类型元素,编译器类型检查会不通过,而且在获取元素时,不需要做类型转换,因为编译器已经帮我们实现了类型转换,转换成字节码就能看到。
private void test() {
List<Integer> list=new ArrayList();
list.add(1);
int data1= list.get(0);
}
例子2代码使用泛型,定义泛型T
//定义
public class Person<T> {
private T data;
public void setData(T data){
this.data=data;
}
public T getData(){
return this.data;
}
}
person定义为 Person<String>类型,setData只能传入String类型参数,如果放入其他类型元素,编译器类型检查会不通过,getData也不需要做类型转换,因为编译器已经帮我们实现了类型转换;
//调用
Person<String> person=new Person();
person.setData("name");
String name=person.getData();
2.泛型的好处
(1)泛型使得类型参数化,也就是把类型当成参数来使用,更具有扩展性,代码复用。列子2中,分别把Integer、String类型当成参数实例化List、Person,List即可以处理Integer类型,也能处理String,甚至自定义类型,Person同理;
(2)参数类型一旦确定,如果类型不匹配,在编译期就能发现,降低异常概率;
3.泛型实现
泛型最终也是靠Object来保存数据的,定义的泛型编译成class文件时会被擦除,这就是类型擦除。例如,List<Integer>编译时候,会将Integer擦除变成List, Person<String> 最后变成Person。虽然泛型被擦除,泛型信息还是会被保存了下来,保存在class字节码常量池中,在使用了泛型的代码处会生成一个signature签名字段指向常量池地址。
4.类型擦除优点
(1)List<Integer>变成List,这样加载到方法区,占内存就会比较小,方法区的压力也就小,而C#的泛型类型在内存中就是真实存在;
(2)Java1.5之前是使用Object,类型擦除就可以兼容之前的代码;
5.泛型缺点
(1)基本数据类型无法作为泛型实参
如果想在list中放入int类型数据,就必现把list定义成List<Integer>,那么在放入取出数据就会涉及到装箱拆箱,这样会对内存有影响(创建对象),而C#可以用基本数据类型作为泛型实参;Android推出SparseArray来替代key为Integer型的Hashmap,这样可以解决装箱拆箱问题;
(2)泛型无法用作方法重载
以下代码编译器会报错,因为类型擦除以后,这两个方法是一样的,而C#则可以;
private void add(List<Integer> data);
private void add(List<String> data);
(3)泛型无法当做真实类型
以下代码编译器会报错(Illegal generic type for instanceof),List<Integer> 不是一个真实类型,类型擦除以后还是List;
private void add(List<Integer> data){
if(data instanceof List<Integer>){
}
}
以下代码编译器同样会报错(Type parameter 'T' cannot be instantiated directly),泛型T不是一个真实类型
private <T>void a(){
T t=new T();
}
(4)静态方法无法引用泛型类型
以下代码编译器会报错(com.yang.memorytest.Person.this' cannot be referenced from a static context),因为泛型类型只有在类实例化的时候才确定,而静态方法可以直接通过类来调用;
public class Person<T> {
public static void a(T data){
}
}
如果静态方法需要使用泛型,可以在方法定义泛型,
public class Person {
public static <T>void a(T data){
}
}
(5)泛型的没有继承关系
public class Person<T> {
private T data;
}
//虽然ArrayList是List的子类
Person<List> person=new Person<ArrayList>();
6.泛型运用-Gson
Gson提供fromJson方法将json转换成实体。虽然方法定义了<T>,但还是需要传入一个 Class<T>类型的参数,方法才能知道具体转换成什么类型,因为方法定义的T泛型最终会被类型擦除。
//调用处
ReadList.BookDetail detail = new Gson().fromJson(object.toString(), ReadList.BookDetail.class);
//源码处
public <T> T fromJson(String json, Class<T> classOfT) throws JsonSyntaxException {
Object object = fromJson(json, (Type) classOfT);
return Primitives.wrap(classOfT).cast(object);
}
7.泛型运用-数据解析
通过反射获取实体类型,利用Gson进行解析。
定义抽象类CommonResultListener<T>,上层实现抽象类,并确定T泛型。CommonResultListener反射获取泛型类型,利用Gson进行解析。
以下代码,CommonResultListener实现了底层网络请求接口,网络请求成功会回调onSuccess方法,我们通过getActualTypeArguments获取泛型类型,最后通过Gson来解析数据。
public abstract class CommonResultListener<T> extends ResultListener {
@Override
public void onSuccess(ResultSupport resultSupport) {
Type genericSuperclass = getClass().getGenericSuperclass();
Type genericityType = ((ParameterizedType) genericSuperclass).getActualTypeArguments()[0];
try {
String data = resultSupport.getModel("data") != null ? resultSupport.getModel("data").toString() : "";
T t = (new Gson()).fromJson(data, genericityType);
callResultSuccess(t);
} catch (Exception e) {
Log.e(TAG, "call back exception!", e);
}
}
....
总结
泛型的好处,代码复用、可扩展性以及降低异常概率;
以上分析有不对的地方,请指出,互相学习,谢谢哦!
网友评论