泛型 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。

我们使用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)

你不得不让他自身去实现一下这个泛型类。而不是用的时候,直接传递。因此,必须将带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

  1. 常用的代码
//序列化:用的最多的
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]) {});