在之前的文章MonkeyLei:Android-Retrofit2+Rxjava2之网络通用请求-初步封装 小白进行了初步封装,后续又正对请求数据做了进一步封装MonkeyLei:Android-Retrofit2+Rxjava2之网络通用请求-初步封装-完善优化数据【对象】请求。
其中有一个地方,就是json数据转换的地方:
我们基本都是判断请求的实体类类型参数,然后进行Json数据转对象的操作。之前小白不知道怎么样可以获取泛型的类型,就比如这样操作:(瞎写的,主要是想表达,不同的泛型最后可以获取对应对象类型,然后就可以进行转换。这样的话,基本上一行代码就可以进行转换了)
比如这样(就搞定了对象的转换,哈哈。。骚的一笔。。。为什么小萌新是如下的结构,我们后面一一说来):
/**
* 更通用的泛型转换
* @param strData
* @param _resultCallBack
* @return
*/
public static Object getBean(String strData, ResultCallBack _resultCallBack) {
//Gson gson = new Gson();
//return gson.fromJson(strData, _resultCallBack.mType);
return JSON.parseObject(strData, _resultCallBack.mType);
}
开始前了,我们要做一些基础知识的实践,就是关于如果获取泛型类型的方法,自己可以用Eclipse实践下(可以装JDk1.8,后面有用)
1. 看下两个东东 Java Platform SE 8 getSuperclass getGenericSuperclass
getSuperclass
public Class<? super T> getSuperclass()
Returns the Class representing the superclass of the entity (class, interface, primitive type or void) represented by this Class. If this Class represents either the Object class, an interface, a primitive type, or void, then null is returned. If this object represents an array class then the Class object representing the Object class is returned.
Returns:
the superclass of the class represented by this object.
getGenericSuperclass
public Type getGenericSuperclass()
Returns the Type representing the direct superclass of the entity (class, interface, primitive type or void) represented by this Class.
If the superclass is a parameterized type, the Type object returned must accurately reflect the actual type parameters used in the source code. The parameterized type representing the superclass is created if it had not been created before. See the declaration of ParameterizedType for the semantics of the creation process for parameterized types. If this Class represents either the Object class, an interface, a primitive type, or void, then null is returned. If this object represents an array class then the Class object representing the Object class is returned.
Returns:
the superclass of the class represented by this object
Throws:
GenericSignatureFormatError - if the generic class signature does not conform to the format specified in The Java™ Virtual Machine Specification
TypeNotPresentException - if the generic superclass refers to a non-existent type declaration
MalformedParameterizedTypeException - if the generic superclass refers to a parameterized type that cannot be instantiated for any reason
Since:
1.5
直接来一篇网友分析,好伐 Class的 getSuperclass与getGenericSuperclass区别、
可能需要多看几遍,以及案例的实践。小萌新的如下测试:
public class TestGeneric {
public static class Person<T> {
}
class Student extends Person<TestBean> {
}
class Student2 extends TestBean/*Person*/ {
}
public static class UserInfoBean {
}
public static void main(String[] args) {
// TODO Auto-generated method stub
System.out.println("Student.class.getSuperclass()\t"
+ Student.class.getSuperclass());
System.out.println("Student.class.getGenericSuperclass()\t"
+ Student.class.getGenericSuperclass());
System.out.println("Student2.class.getGenericSuperclass()\t"
+ Student2.class.getGenericSuperclass());
System.out.println("UserInfoBean.class.getGenericSuperclass()\t"
+ UserInfoBean.class.getGenericSuperclass());
Person person = new Person();
System.out.println("person.class.getGenericSuperclass()\t"
+ person.getClass().getGenericSuperclass());
UserInfoBean userBean = new UserInfoBean();
System.out.println("userBean.class.getGenericSuperclass()\t"
+ userBean.getClass().getGenericSuperclass());
}
}
结果:也就是说,这个getGenericSuperclass可以获取类的超类的Type,包含泛型参数!但不能是Object类、接口、基本类型、 void、或实体类。
1.1 然后我们就要考虑上面的练习的案例中可以想出什么方式来实现这个泛型的获取。
1.2 这个时候需要一个知识ParameterizedType
public interface ParameterizedType
extends Type
ParameterizedType represents a parameterized type such as Collection<String>.
A parameterized type is created the first time it is needed by a reflective method, as specified in this package. When a parameterized type p is created, the generic type declaration that p instantiates is resolved, and all type arguments of p are created recursively. See TypeVariable for details on the creation process for type variables. Repeated creation of a parameterized type has no effect.
Instances of classes that implement this interface must implement an equals() method that equates any two instances that share the same generic type declaration and have equal type parameters.
Since:
1.5
1.3 实际上我们通过getGenericSuperclass获取的类型就是ParameterizedType,然后就可以其getActualTypeArguments方法获取到对应的泛型类型,就是数组的第0个值。
getActualTypeArguments
Type[] getActualTypeArguments()
Returns an array of Type objects representing the actual type arguments to this type.
Note that in some cases, the returned array be empty. This can occur if this type represents a non-parameterized type nested within a parameterized type.
Returns:
an array of Type objects representing the actual type arguments to this type
Throws:
TypeNotPresentException - if any of the actual type arguments refers to a non-existent type declaration
MalformedParameterizedTypeException - if any of the actual type parameters refer to a parameterized type that cannot be instantiated for any reason
Since:
1.5
1.4 然后请关注一个知识 泛型,TypeToken(Guava)其中的Types.canonicalize(parameterized.getActualTypeArguments()[0]),这个就是获取,就是泛型类型转换Type的关键点。这里用的是Gson进行转换。 这样得到的Type将可以作为Gson数据转换使用。解决了我们只能靠判断写死的情况:
实际上大概是这样:
Student student = new Student();
ParameterizedType parameterized = (ParameterizedType) student.getClass().getGenericSuperclass();
System.out.println("student.class.getGenericSuperclass()\t"
+ $Gson$Types
.canonicalize(parameterized.getActualTypeArguments()[0]));
1.5. 我们封装下可以定义一个ResultCallBack抽象类,还可以额外提供其他回调方法。
ResultCallBack.java Eclispe中gson库记得导入下,谢谢!AS的话不用,可以直接使用!
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import com.google.gson.internal.$Gson$Types;
/**
* @intro 获取泛型实体类类型
* @author hl
*
* @param <T>
*/
public abstract class ResultCallBack<T> {
public Type mType;
public ResultCallBack() {
mType = getSuperclassTypeParameter(getClass());
//mType.getTypeName();
}
public static Type getSuperclassTypeParameter(Class<?> subclass) {
Type superClass = subclass.getGenericSuperclass();
if (superClass instanceof Class) {
System.out.println("superClass=" + superClass);
throw new RuntimeException("Missing type parameter.");
}
ParameterizedType parameterized = (ParameterizedType) superClass;
return $Gson$Types
.canonicalize(parameterized.getActualTypeArguments()[0]);
}
//public abstract void onResponse(T response);
}
然后试验下呗:TestGeneric.java
public static void main(String[] args) {
// TODO Auto-generated method stub
ResultCallBack<TestBean> resultCallBack = new ResultCallBack<TestBean>() {};
System.out.println("tg=" + resultCallBack.mType);
System.out.println("getTypeName=" + resultCallBack.mType.getTypeName());
System.out.println("getTypeName=" + resultCallBack.mType.getClass().getName() + "@" + Integer.toHexString(resultCallBack.mType.getClass().hashCode()));
System.out.println("getTypeName=" + TestBean.class.getName());
System.out.println("mType.getClass().getName=" + resultCallBack.mType.getClass().getName());
System.out.println("getClass=" + resultCallBack.getClass());
System.out.println("getName=" + resultCallBack.getClass().getName());
Gson gson = new Gson();
TestBean testBean = gson.fromJson("{\"name\":\"test_萱妹儿\"}", resultCallBack.mType);
System.out.println("getName=" + testBean.getName());
}
结果:resultCallBack.mType就可以用来做Gson、fastjson解析参数了。饿哦哦哦...
这样的话,我们的之前的封装MonkeyLei:Android-Retrofit2+Rxjava2之网络通用请求-初步封装 就可以做简化,以ResultCallBack作为参数类型,而且JDK1.8中还有resultCallBack.mType.getTypeName()可以获取类全名称(包含包名路径)字符串,这样如果有特殊的类需要处理后进行转换,我们就可以特别判断下
比如如下判断 typeName = resultCallBack.mType.getTypeName()?:
但是小萌新发现AS中并没有这个方法,也就是说可能jdk没有用最新的。不配置最新JDK也行,可以这样:
String typeName = _resultCallBack.mType.toString().replace(" ", "").replaceFirst("class", "");
强行可以获取类似“com.generic.test.TestBean”的类全路径名称。然后就可以处理特殊情况:
/**
* 更通用的泛型转换
* @param strData
* @param _resultCallBack
* @return
*/
public static Object getBean(String strData, ResultCallBack _resultCallBack) {
String typeName = _resultCallBack.mType.toString().replace(" ", "").replaceFirst("class", "");
///< 资讯详情页相关
if (typeName.equals(ShareResultBean.class.getName())){
if (null == strData || strData.equals("null")){
return new ShareResultBean("", 0, 0);
}
}else if (typeName.equals(ThumbpResultBean.class.getName())){
if (null == strData || strData.equals("null")){
return new ThumbpResultBean("", 0, 0);
}
}else if (typeName.equals(SuanliCountBean.class.getName())){
return new SuanliCountBean(strData);
}else if (typeName.equals(RewardAuthorBean.class.getName())){
if (null == strData || strData.equals("null")){
return new RewardAuthorBean("");
}
}else if (typeName.equals(CommitResultBean.class.getName())){
if (null == strData || strData.equals("null")){
return new CommitResultBean("", 0, 0);
}
}
///< 快讯
else if (typeName.equals(GoodBadResultBean.class.getName())){
if (null == strData || strData.equals("null")){
return new GoodBadResultBean("", 0, 0);
}
}
/**
* 分享 - 获取的是图片url地址
*/
else if (typeName.equals(NewsShareBean.class.getName())){
return new NewsShareBean(strData);
}
else if (typeName.equals(CandyShareBean.class.getName())){
return new CandyShareBean(strData);
}
//Gson gson = new Gson();
//return gson.fromJson(strData, _resultCallBack.mType);
return JSON.parseObject(strData, _resultCallBack.mType);
}
使用的地方做调整就好了,其他的代码请看之前的文章就好,重点是思路:
最终网络请求的地方:
增加了一个抽象类对象作为参数(该抽象类可以添加回调扩展哟。。。)。 第一个参数为什么要传是因为我们的url是用这个来映射的:
实际上可以不用传,用_resultCallBack.mType.toString()的值,比如“class com.test.xxxx"作为key映射,这样就可以利用_resultCallBack对应的mType的处理后的值作为key获取请求url。
不管上面说的又多烂,有多没有逻辑,最终我们只需要关注几个地方:
a ResultCallBack.java 类的实现
b Json的转换, gson、fastjson,摒弃TypeRefrence..
c 顺便看下fastjson的TypeReference实现 - 熟悉吧,啊哈哈哈。。我也是今天总结才看了哈!嘻嘻~~~
put与putIfAbsent区别-我的JAVA世界-51CTO博客
不知道为什么android环境需要保存这个东东,然后获取,不懂,哈哈哈。。。又涨了一波姿势。。
你妹的,不会的东西也特多了!! 到目前为止,网络请求,rec通用适配器两大块通用封装基板完成。代码完整性和完善性还不得劲,差蛮多的。 不过小萌新看了下有个通用适配器的第三方开源库的设计思路,我的貌似有点像那种思路,那就证明了方向应该还是ok的。毕竟我的工程都重构完了,基本运行也ok,目前没什么大问题,还准备上线试试。
之后的新工程就打算直接上纲上线了。。。虽然不能总是造轮子,但是还是的学习!
补充:上面的 ResultCallBack.java 针对列表也可以哟!
import java.lang.reflect.ParameterizedType;
import java.util.List;
import com.google.gson.Gson;
import com.google.gson.internal.$Gson$Types;
...........
ResultCallBack<List<TestBean>> resultCallBack2 = new ResultCallBack<List<TestBean>>() {};
System.out.println("tg2=" + resultCallBack2.mType);
System.out.println("tg2=" + resultCallBack2.mType.getTypeName());
Gson gson2 = new Gson();
List<TestBean> testBean2 = gson2.fromJson("[{\"name\": \"test_萱妹儿2\"}, {\"name\": \"test_萱妹儿2\"}]", resultCallBack2.mType);//new TypeToken<List<TestBean>>() {}.getType());
System.out.println("getName2=" + testBean2.get(0).getName());
结果
完美,这样下来其实我们json转换理论上就一个方法就可以(小萌新之所以没有这样
是因为,我过程中需要针对数据做处理, 所以没有直接就转对象,而是倒腾了一下...):