引言
在讲阿里fastjson 之前,先讲下泛型的一些基础知识和在反射中如何获取泛型,觉得自己已经掌握的可以直接通过目录跳到最后查看
泛型类
泛型类的定义只要在申明类的时候,在类名后面直接加上< E>,中的E可以是任意的字母,也可以多个,多个用逗号隔开就可以。示例代码如下
public class SelfList<E> {}
复制代码
泛型类中的实际类型的推断
那么什么时候确定这个E 的具体类型呢?其实是在new 一个对象的时候指定的,请看下面代码
public class SelfList<E> {
public void add(E e) {
if (e instanceof String) {
System.out.println(" I am String");
} else if (e instanceof Integer) {
System.out.println("I am Integer");
}
}
public static void main(String[] args) {
//这里创建的时候指定了String
SelfList<String> a = new SelfList<String>();
a.add("123");
//这里创建的时候指定了Integer
SelfList<Integer> b = new SelfList<Integer>();
b.add(123);
}
}
复制代码
泛型接口
泛型接口和类的使用方式一样
public interface IndicatorTypeService<T> {}
//这里定义的时候指定具体类型为ProductSale,当然也可以这里没有指定具体类型
public class ProductSaleServiceImpl implements IndicatorTypeService<ProductSale> {}
复制代码
泛型方法
这个我觉得是相对来说比较难得,大家集中注意力听我说,说不定你以前一直以为的泛型方法是假的。好,先给个假的泛型方法给大家验一下,还是上面代码的例子,为了方便阅读我再贴一遍代码
//注意这是个假的泛型方法,不要以为有一个E就是泛型方法哦
public void add(E e) {
if (e instanceof String) {
System.out.println(" I am String");
} else if (e instanceof Integer) {
System.out.println("I am Integer");
}
}
复制代码
泛型方法的定义
好了,重点来了,给个真正的泛型方法定义出来
public <T> T get(T t) {
return t;
}
复制代码
- 返回值和public 之间的< T> 是泛型方法的必要条件,并且这个和类的定义的泛型E 是可以同名(一般设置不同名),并且他们之间是独立的。
- < T> 可以多个,多个用逗号隔开,列如 <T,V>
- 返回值不一定是T,可以是任意的类型,如Long
- 方法中的参数也不一定是T,可以是任意的类型,如Long。只是泛型方法一般返回值类型和参数有其中一个是定义的泛型(全是具体类型就没意义了)
public <T> T get(T t) {
return t;
}
public static void main(String[] args) {
//这里创建的时候指定了String
SelfList<String> a = new SelfList<String>();
a.add("123");
int num = a.get(123);
}
复制代码
泛型方法中的实际泛型的推断
那么泛型方法是怎么确定这个具体类型的呢? 主要思想是在调用该泛型方法传进去的参数类型和返回值类型来确定具体类型的
- 泛型变量在参数列表中只出现一次,调用该方法时根据传进行的实参类型来确定
public <T> T get1(T t) {
if (t instanceof String) {
System.out.println(" I am String");
} else if (t instanceof Integer) {
System.out.println("I am Integer");
}
return t;
}
public static void main(String[] args) {
SelfList<String> a = new SelfList<String>();
//这里调用的时候传进去的是int 类型,所以确定了他的类型是int
int b=a.get1(123);
}
复制代码
输出结果
I am Integer
复制代码
- 当参数列表中有多个参数使用了相同的泛型变量,返回值类型也使用了该变量,那么返回值类型由他们的公共父类来决定最终的泛型类型
public <T> T get2(T t, T t2) {
if (t instanceof Float) {
System.out.println(" I am String");
} else if (t instanceof Integer) {
System.out.println("I am Integer");
} else if (t instanceof Number) {
System.out.println("I am Number");
}
return t;
}
public static void main(String[] args) {
SelfList<String> a = new SelfList<String>();
//这里返回值类型必须是number 类型
Number b = a.get2(123, 12.1);
}
复制代码
注意上面的输出还会是
I am Integer
复制代码
因为根据第一条规则,传进去的是什么类型就是什么类型,但是返回值类型候需要根据第二条规则来确定
反射中的泛型使用
上面说的都是在编译之前就可以确定的泛型。大家知道,泛型运行的时候其实是会被擦除的。不过没关系,还是提供给我们通过反射的方式来获取。首先我来认识下java中的泛型类型继承结构
这里主要讲平时运用最多的三个类,其他还有一些GenericArrayType 之类的就不讲了,大家按着我这个分析的思路去看下就可以ParameterizedType 源码
public interface ParameterizedType extends Type {
/**
* 返回一个实际类型的数组
* 比如对于ArrayList<T>,被实例化的时候ArrayList<String>,这里返回的就是String
*
*/
Type[] getActualTypeArguments();
/**
* 这里其实就是返回去掉泛型后的真实类型
* 对于List<T> 这里返回就是List
*/
Type getRawType();
/**
* 这里针对的是内部类的情况,返回的是他的外层的类的类型
* 例如SelfHashMap 里面有一个Entry 内部类,也就是SelfHashMap.Entry<String,String>
* 返回的就是SelfHashMap
*/
Type getOwnerType();
}
复制代码
ParameterizedType 测试验证
定义一个有内部类的类
public class SelfHashMap {
public static class Entry<T, V extends Collection> {
}
}
复制代码
写一个测试类
public class ParameterizedTypeTest {
public static void main(String[] args) {
Class clazz = A.class;
//注意这里是拿父类的泛型,jdk 没有提供本类的泛型
Type type = clazz.getGenericSuperclass();
if (type instanceof ParameterizedType) {
ParameterizedType parameterizedType = (ParameterizedType) type;
System.out.println(Arrays.toString(parameterizedType.getActualTypeArguments()));
System.out.println(parameterizedType.getRawType());
System.out.println(parameterizedType.getOwnerType());
}
}
/**
* 这里的泛型被是指定了为String 和List
* getActualTypeArguments() 会输出 String 和List
*/
public static class A extends SelfHashMap.Entry<String, List> {
}
}
复制代码
输出结果
[class java.lang.String, interface java.util.List]
class com.wizhuo.jdklearning.reflect.dto.SelfHashMap$Entry
class com.wizhuo.jdklearning.reflect.dto.SelfHashMap
复制代码
TypeVariable 源码
public interface TypeVariable<D extends GenericDeclaration> extends Type, AnnotatedElement {
/**
* 这里返回的是泛型的上界类型数组,比如 <T, V extends Collection> 这里的上界类型就是Collection
*/
Type[] getBounds();
/**
* 返回的是声明该泛型变量的类,接口,方法等
* 列如 public static class Entry<T, V extends Collection> 返回的就是Entry
*/
D getGenericDeclaration();
/**
* 这里返回的就是泛型定义的变量名称,比如 <T> 返回的就是T
*
*/
String getName();
/**
* 这里返回的就是AnnotatedType 数组,jdk1.8 才加进来,本文不分析,直接跳过
*/
AnnotatedType[] getAnnotatedBounds();
}
复制代码
写一个测试类
public class TypeVariableTest {
public static void main(String[] args) {
Class clazz = SelfHashMap.Entry.class;
TypeVariable[] typeVariables = clazz.getTypeParameters();
for (TypeVariable ty :typeVariables) {
System.out.println(ty.getName());
System.out.println(Arrays.toString(ty.getAnnotatedBounds()));
System.out.println((Arrays.toString(ty.getBounds())));
System.out.println(ty.getGenericDeclaration());
System.out.println("============================");
}
}
}
复制代码
输出的结果
T
[sun.reflect.annotation.AnnotatedTypeFactory$AnnotatedTypeBaseImpl@3fee733d]
[class java.lang.Object]
class com.wizhuo.jdklearning.reflect.dto.SelfHashMap$Entry
============================
V
[sun.reflect.annotation.AnnotatedTypeFactory$AnnotatedTypeBaseImpl@5acf9800]
[interface java.util.Collection]
class com.wizhuo.jdklearning.reflect.dto.SelfHashMap$Entry
复制代码
阿里fastjson 泛型的巧妙运用
看到下面这段代码,简单的意思就是从redis 获取字符串,然后转换为指定的泛型的类。大家可以留意到这里创建了个TypeReference 匿名类 注意后面是有带{}的,所以是实例化一个匿名内部类(这是重点中的重点,一切的魔术从这里开始),而不是TypeReference 这个类的实例
List<ResourceEntity> resources =
redisAdapter.get(BaseConstant.TENANT_CODE_SYSTEM, CacheKey.KEY_ALL_RESOURCE,
new TypeReference<List<ResourceEntity>>() {
});
复制代码
窥探源码
点进去看看这个源码
protected final Type type;
/**
*注意这里protected ,也就意味着我们创建的时候只能继承这个类成为他的子类,毕竟我们的类
* 不可能和阿里巴巴的fastjson 在同一个包目录下
*
* 一般我们都是创建一个匿名内部类来成为他的子类,然后泛型中传进我们想要转化的最终泛型
* 例如上面的代码new TypeReference<List<ResourceEntity>>() {} List<ResourceEntity> 是我们想要转化的类型
*
*
*/
protected TypeReference(){
//这里是获取父类的泛型,由于jdk 不支持获取自己的泛型,这里巧妙的通过继承这个类,变成获取父类的泛型来解决
Type superClass = getClass().getGenericSuperclass();
//这里返回泛型的实际类型,就是 List<ResourceEntity>
type = ((ParameterizedType) superClass).getActualTypeArguments()[0];
}
复制代码
看完两件事
如果你觉得这篇内容对你挺有启发,我想邀请你帮我2个小忙:
- 点赞,让更多的人也能看到这篇内容(收藏不点赞,都是耍流氓 -_-)
- 关注公众号「面试bat」,不定期分享原创知识,原创不易,请多支持(里面还提供刷题小程序哦)。