Android-Java-泛型-获取泛型类型-ParameterizedType

在之前的文章MonkeyLei:Android-Retrofit2+Rxjava2之网络通用请求-初步封装 小白进行了初步封装,后续又正对请求数据做了进一步封装MonkeyLei:Android-Retrofit2+Rxjava2之网络通用请求-初步封装-完善优化数据【对象】请求

其中有一个地方,就是json数据转换的地方:

image

我们基本都是判断请求的实体类类型参数,然后进行Json数据转对象的操作。之前小白不知道怎么样可以获取泛型的类型,就比如这样操作:(瞎写的,主要是想表达,不同的泛型最后可以获取对应对象类型,然后就可以进行转换。这样的话,基本上一行代码就可以进行转换了)

image

比如这样(就搞定了对象的转换,哈哈。。骚的一笔。。。为什么小萌新是如下的结构,我们后面一一说来):

 /**
     *  更通用的泛型转换
     * @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、或实体类。

image

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)其中的GsonTypes.canonicalize(parameterized.getActualTypeArguments()[0]),这个就是获取,就是泛型类型转换Type的关键点。这里用的是Gson进行转换。 这样得到的Type将可以作为Gson数据转换使用。解决了我们只能靠判断写死的情况:

image

实际上大概是这样:

        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的话不用,可以直接使用!

image
 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解析参数了。饿哦哦哦...

image

这样的话,我们的之前的封装MonkeyLei:Android-Retrofit2+Rxjava2之网络通用请求-初步封装 就可以做简化,以ResultCallBack作为参数类型,而且JDK1.8中还有resultCallBack.mType.getTypeName()可以获取类全名称(包含包名路径)字符串,这样如果有特殊的类需要处理后进行转换,我们就可以特别判断下

比如如下判断 typeName = resultCallBack.mType.getTypeName()?

image

但是小萌新发现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);
    }

使用的地方做调整就好了,其他的代码请看之前的文章就好,重点是思路:

image

最终网络请求的地方:

image

增加了一个抽象类对象作为参数(该抽象类可以添加回调扩展哟。。。)。 第一个参数为什么要传是因为我们的url是用这个来映射的:

image

实际上可以不用传,用_resultCallBack.mType.toString()的值,比如“class com.test.xxxx"作为key映射,这样就可以利用_resultCallBack对应的mType的处理后的值作为key获取请求url。

不管上面说的又多烂,有多没有逻辑,最终我们只需要关注几个地方:

a ResultCallBack.java 类的实现

image

b Json的转换, gson、fastjson,摒弃TypeRefrence..

image

c 顺便看下fastjson的TypeReference实现 - 熟悉吧,啊哈哈哈。。我也是今天总结才看了哈!嘻嘻~~~

image

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());

结果

image

完美,这样下来其实我们json转换理论上就一个方法就可以(小萌新之所以没有这样

image

是因为,我过程中需要针对数据做处理, 所以没有直接就转对象,而是倒腾了一下...):

image
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,732评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,496评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,264评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,807评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,806评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,675评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,029评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,683评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,704评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,666评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,773评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,413评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,016评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,978评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,204评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,083评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,503评论 2 343

推荐阅读更多精彩内容