泛型擦除

一、使用泛型的好处

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

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

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

image.png

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

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

首先,声明一个泛型类

public class Test {

    protected T dataT;

    protected E dataE;

    protected V dataV;
}

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

public class Student extends Test {
    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 {

}

class Student2 extends Person2 {

}
image.png

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

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

再来看一段代码

public static void main(String[] args) {
        List list = new ArrayList();
        Map map = new HashMap();
        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 {

}
image.png

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

public static void main(String[] args) {
        Map map = new HashMap() {
        };
        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 foo = new Foo();
        foo.value = bean;
        Log.e("MainActivity", "foo jsonStr: " + gson.toJson(foo));

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

    class DataBean{
        public  String name;
    }

    class Foo {
        T value;
    }
}

fastjson使用的TypeReference

//通过创建TypeReference的匿名内部类的方式来保留反省信息,以便json反序列化时能反射获取到泛型实际类型
ResponseDTO responseDTO = JSONObject.parseObject(jsonString, new TypeReference>() {});

你可能感兴趣的:(泛型擦除)