美文网首页
泛型擦除

泛型擦除

作者: 苏苏苏苏考拉 | 来源:发表于2020-02-28 14:07 被阅读0次

    一、使用泛型的好处

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

    相关文章

      网友评论

          本文标题:泛型擦除

          本文链接:https://www.haomeiwen.com/subject/zxykhhtx.html