泛型 Generics
对于java的泛型一般接触比较少。经常会碰到一些函数参数,Class<?> class,Class 一脸懵逼的状态。
本文的目的,是了解一下泛型。顺便将一些工作中遇到的疑惑编写出来。
几个平时见到比较奇怪的东西,疑惑:
//java中几个不熟悉的东西:
<T>,
<T extends xxxx>
<?>,
<? extends Class>,
Class<?>
//fastjson中的一些不熟悉的东西:
xxx
fastjson.JSONObject jo = JSON.parseObject(remoteResultJson);
jo.remove("mtopStat");
xResponse = JSON.parseObject(jo.toJSONString(), XXXResponse.class);
out = JSON.parseObject(xResponse.getBytedata(), outClass);
ApiResponse<T> apiResponse;
try {
Class callbackClass = callback.getClass();
Type[] types = callbackClass.getGenericInterfaces();
Type type;
if (types == null || types.length <= 0) {
type = callbackClass.getGenericSuperclass();
} else {
type = types[0];
}
Type[] params = ((ParameterizedType)type).getActualTypeArguments();
String content = new String(jsonData, StandardCharsets.UTF_8);
apiResponse = JSON.parseObject(content,
new TypeReference<ApiResponse<T>>(params[0]) {});
} catch (Throwable e) {
// empty
callback.onError(UNKNOWN_CLIENT_MAPPING_CODE_SUFFIX, e.getLocalizedMessage());
return;
}
从头开始了解一些东西吧。
Type
先来了解下Type。详细参考连接Java中的Type。
我们使用Type主要的目的是,在使用泛型的时候,通过各种方式,获取到Class,进而获取到某个参数它的实体类型,进而继续反序列化。
1.5之前没有泛型,在1.5之后加入的;为了兼容了之前的设计,与其他语言泛型能在设计之初设计出更好的方案而言,java泛型在编译后会被擦除,字节码是不带泛型的。因此java抽象了对所有的基本类型、引用类型向上的抽象。Type是java中所有类型的公共高级接口。他们包含原始类型Class,参数化类型ParameterizedType,数组类型GenericArrayType,类型变量TypeVariable,基本类型int、float等。
Type
: ParameterizedType | TypeVariable | Class | GenericArrayType
在java里面,Type和其他四个子类型,均是用来描述各个形式的类型,包括参数,变量,数组,或者像Map<K,V>这样的一个整体。
ParameterizedType
”参数化类型“,可以理解为凡是带了尖括号<T>, <?>
的类类型,就是ParameterizedType,而不带的就不是。
Map<String, Person> map;
Set<String> set1;
Class<?> clz;
Holder<String> holder;
List<String> list;
static class Holder<V>{
}
//不是
Set set;
List aList;
getActualTypeArguments() : 返回的是Type[], 他的目的是为了获取这个ParameterizedType总共几个泛型。我们在封装gson、fastjson的时候,编写一些自定义解析最重要的就是通过这个方法,获取到泛型的类型,并指明第几个,
Type[] params = ((ParameterizedType)type).getActualTypeArguments();
gson.fromJson(content, params[0]); //gson使用
JSON.parseObject(content, new TypeReference<YourBean>(params[0]) {}); //fastjson
getRawType() :
getOwnerType() :
TypeVariable
“类型变量”,指代的是泛型中的变量,而ParameterizedType指代的是整个泛型。=
getName(),
getBounds(),
getGenericDeclaration()
GenericArrayType
泛型数组类型,描述ParameterizedType或者TypeVariable的数组。
getGenericComponentType()
常见问题
1.
Exception in thread “main” java.lang.ClassCastException: java.lang.Class cannot be cast to java.lang.reflect.ParameterizedType
at A.getMyClass(MainActivity.java:70)
at MainActivity.main(MainActivity.java:110)
参考 https://blog.csdn.net/whoami_i/article/details/94021759 你不得不让他自身去实现一下这个泛型类。而不是用的时候,直接传递。因此,必须将带T的class设置为abstract这样要求他必须去实现一个子类,才能使用;或者通过new BaseClass() {},加上中括号就可以执行子类了。
也是因为这个原因,使用fastjson,经常看到这样的代码:
JSON.parseObject(content, new TypeReference<YourBean>(params[0]) {}); //fastjson
2.
TypeReference的理解,可以参考这一篇;我们很容易有个疑惑,为什么不直接传入params[0]就好了,何必要再封装一层呢?
我也初略地阅读了他的代码,TypeReference主要的目的就是强制把YourBean封装到一个类里面,而且由于前面说的,通过子类才能查询到泛型,内部通过
目的也是因为只有子类才能
public Class<?>[] getInterfaces()
获取由此对象表示的类或接口实现的接口
public reflect.Type[] getGenericInterfaces()
获取由此对象表示的类或接口直接实现的接口的Type。
从名字来看,generic指代是泛型,所以跟泛型是有关的;
从返回值来看,一个是返回的是Class数组;一个返回的是Type数组;
从内部代码执行来看,getGenericInterfaces会复杂一些,它把泛型参数给解析出来了。
同理,
public Class<? super T> getSuperclass()
, public Type getGenericSuperclass()
获取父类信息,但是后者会带上泛型参数。
FastJson
- 常用的代码:
//序列化:用的最多的
String = JSON.toJSONString(Object); //参数很广泛,可以是类bean实例对象,也可以是JSONObject,也可以是Map等等。
//返回结果就是String
//反序列化:最常用的一个方式:
T t = JSON.parseObject(String, Class<T>); //传入jsonStr和xxx.class,返回的结果就是实例化后的对象
JSONObject jo = JSON.parseObject(String); //如果只传入一个jsonStr,则返回的是JSONOBject
//如果现在被前面这个函数转为了jo,我们继续为T,可以如此。
xxx = JSON.parseObject(jo.toJSONString(), xxx.class));
复杂用法
JSON.parseObject(String, Class)都是在我们已经知道Class的实体类情况下,传递给他的xxx.class解析出来对象的。现在,我们要通过泛型传入。做在SDK中。因此需要了解前面提到的反射获取泛型。
Class callbackClass = callback.getClass();
Type[] types = callbackClass.getGenericInterfaces();
Type type;
if (types == null || types.length <= 0) {
type = callbackClass.getGenericSuperclass();
} else {
type = types[0];
}
Type[] params = ((ParameterizedType)type).getActualTypeArguments();
String content = new String(jsonData, StandardCharsets.UTF_8);
apiResponse = JSON.parseObject(content, new TypeReference<ApiResponse<T>>(params[0]) {});