泛型擦除

一、使用泛型的好处

1.泛型确定了具体的数据类型,对于List,Map这样的数据容器提供了类型检测机制,只有相匹配的数据才能正常的赋值,否则编译器就不通过,它是一种类型安全检测机制,一定程度上提高了软件的安全性防止出现低级的失误
2.不必等到运行的时候才去强制转换,可以直接获取到相应的数据类型,程序更加可读。

public static void main(String[] args) {
        Class c1 = new ArrayList<Integer>().getClass();
        Class c2 = new ArrayList<String>().getClass();

        System.out.println(c1 == c2);
        System.out.println(c1);
    }

image.png

但是不管是ArrayList<Integer>还是ArrayList<String>,在编译时都会被编译器擦除成了ArrayList.

那么如何获取泛型参数的实际类型呢。由于通过getClass无法直接拿到该泛型的实际类型,但是在程序运行时,类型是确定的,我们可以借助反射来达到我们的目的。

首先,声明一个泛型类

public class Test<T, E, V> {

    protected T dataT;

    protected E dataE;

    protected V dataV;
}

再声明一个继承该泛型的子类

public class Student extends Test<Student, Integer, String> {
    private void test() {
        Student dataT = this.dataT.dataT.dataT;
    }

    public static void main(String[] args) {
        Student student = new Student();
        Class clazz = student.getClass();
        //获得该类的父类
        System.out.println(clazz.getSuperclass());

        //获得带有泛型的父类
        Type type = clazz.getGenericSuperclass();
        System.out.println(type);

        System.out.println("-------");
        ParameterizedType parameterizedType = (ParameterizedType) type;
        //参数化类型数组,泛型可能有多个
        for (Type typeArgument : parameterizedType.getActualTypeArguments()) {
            System.out.println(typeArgument.getTypeName());
        }
    }
}


从上图可以看出,我们可以借助反射通过getGenericSuperclass获得父类泛型再强转为ParameterizedType可以获得参数化类型数组,并且得到所有的泛型类型

同时我们也可以看到getSuperclass和getGenericSuperclass的区别
getSuperclass返回直接继承的父类,但是由于泛型擦除,没有显示泛型参数。
getGenericSuperclass返回直接继承的父类,包含泛型参数

public class Test2 {

    public static void main(String[] args) {
        System.out.println("Student2.class.getSuperclass()--被泛型擦除的父类---" + Student2.class.getSuperclass());
        System.out.println("Student2.class.getSuperclass()--没有被泛型擦除的父类---" + Student2.class.getGenericSuperclass());
        System.out.println("------");
        System.out.println("Test2.class.getSuperclass()--被泛型擦除的父类---" + Test2.class.getSuperclass());
        System.out.println("Test2.class.getSuperclass()--没有被泛型擦除的父类---" + Test2.class.getGenericSuperclass());
        System.out.println("------");
        System.out.println("Object.class.getSuperclass()--被泛型擦除的父类---" + Object.class.getSuperclass());
        System.out.println("Object.class.getSuperclass()--没有被泛型擦除的父类---" + Object.class.getGenericSuperclass());
        System.out.println("------");
        System.out.println("int[].class.getSuperclass()--被泛型擦除的父类---" + int[].class.getSuperclass());
        System.out.println("int[].class.getSuperclass()--没有被泛型擦除的父类---" + int[].class.getGenericSuperclass());
        System.out.println("------");
        System.out.println("void.class.getSuperclass()--被泛型擦除的父类---" + void.class.getSuperclass());
        System.out.println("void.class.getSuperclass()--没有被泛型擦除的父类---" + void.class.getGenericSuperclass());

    }
}


class Person2<T> {

}

class Student2 extends Person2<Test2> {

}
image.png

如果此 Class 表示 Object 类、接口、基本类型或 void,则返回 null。

如果此对象表示一个数组类,则返回表示 Object 类的 Class 对象。

再来看一段代码

public static void main(String[] args) {
        List<Integer> list = new ArrayList<Integer>();
        Map<Integer, String> map = new HashMap<Integer, String>();
        System.out.println(Arrays.toString(list.getClass().getTypeParameters()));
        System.out.println(Arrays.toString(map.getClass().getTypeParameters()));
    }

这种声明和getTypeParameters 方法仍然无法拿到具体的数据类型


image.png

通过上面的解释,我们知道可以通过子类继承父类的方法getGenericSuperclass再获取type类型获取泛型类型。

public class Test2 {

    public static void main(String[] args) {
        MyHashMap map = new MyHashMap();
        System.out.println(map.getClass().getGenericSuperclass());
        ParameterizedType genericSuperclass = (ParameterizedType) map.getClass().getGenericSuperclass();
        for (Type a : genericSuperclass.getActualTypeArguments()) {
            System.out.println(a);
        }

    }
}


class MyHashMap extends HashMap<Integer, String> {

}
image.png

但是每次都要声明子类继承父类的方式会不会太繁琐呢,我们可以使用匿名内部类简化这一步骤

public static void main(String[] args) {
        Map<String, Integer> map = new HashMap<String, Integer>() {
        };
        System.out.println(map.getClass().getGenericSuperclass());
        ParameterizedType genericSuperclass = (ParameterizedType) map.getClass().getGenericSuperclass();
        for (Type a : genericSuperclass.getActualTypeArguments()) {
            System.out.println(a);
        }
    }
image.png

所以无论是gson还是fastjson都是使用匿名内部类巧妙的获取被擦除的泛型信息。
gson是使用TypeToken

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        String jsonData = "{\n" +
                "    \"name\": \"BeJson\"}";
        Gson gson = new Gson();
        DataBean bean = gson.fromJson(jsonData, DataBean.class);
        Log.e("MainActivity", "bean name: " + bean.name);
        Log.e("MainActivity", "bean jsonStr: " + gson.toJson(bean));

        Foo<DataBean> foo = new Foo<DataBean>();
        foo.value = bean;
        Log.e("MainActivity", "foo jsonStr: " + gson.toJson(foo));

        String genericData = "{\"value\":{\"name\":\"BeGeneric\"}}";
        TypeToken<Foo<DataBean>> typeToken = new TypeToken<Foo<DataBean>>(){};
        Foo<DataBean> genericBean = gson.fromJson(genericData, typeToken.getType());
        Log.e("MainActivity", "generic bean value : " + gson.toJson(genericBean.value));
    }

    class DataBean{
        public  String name;
    }

    class Foo<T> {
        T value;
    }
}

fastjson使用的TypeReference

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

推荐阅读更多精彩内容