泛型擦除

一、使用泛型的好處

1.泛型確定了具體的數(shù)據(jù)類型,對于List,Map這樣的數(shù)據(jù)容器提供了類型檢測機(jī)制,只有相匹配的數(shù)據(jù)才能正常的賦值,否則編譯器就不通過,它是一種類型安全檢測機(jī)制,一定程度上提高了軟件的安全性防止出現(xiàn)低級的失誤
2.不必等到運(yùn)行的時候才去強(qiáng)制轉(zhuǎn)換,可以直接獲取到相應(yīng)的數(shù)據(jù)類型,程序更加可讀。

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.

那么如何獲取泛型參數(shù)的實(shí)際類型呢。由于通過getClass無法直接拿到該泛型的實(shí)際類型,但是在程序運(yùn)行時,類型是確定的,我們可以借助反射來達(dá)到我們的目的。

首先,聲明一個泛型類

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;
        //參數(shù)化類型數(shù)組,泛型可能有多個
        for (Type typeArgument : parameterizedType.getActualTypeArguments()) {
            System.out.println(typeArgument.getTypeName());
        }
    }
}


從上圖可以看出,我們可以借助反射通過getGenericSuperclass獲得父類泛型再強(qiáng)轉(zhuǎn)為ParameterizedType可以獲得參數(shù)化類型數(shù)組,并且得到所有的泛型類型

同時我們也可以看到getSuperclass和getGenericSuperclass的區(qū)別
getSuperclass返回直接繼承的父類,但是由于泛型擦除,沒有顯示泛型參數(shù)。
getGenericSuperclass返回直接繼承的父類,包含泛型參數(shù)

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。

如果此對象表示一個數(shù)組類,則返回表示 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 方法仍然無法拿到具體的數(shù)據(jù)類型


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

但是每次都要聲明子類繼承父類的方式會不會太繁瑣呢,我們可以使用匿名內(nèi)部類簡化這一步驟

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都是使用匿名內(nèi)部類巧妙的獲取被擦除的泛型信息。
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

//通過創(chuàng)建TypeReference的匿名內(nèi)部類的方式來保留反省信息,以便json反序列化時能反射獲取到泛型實(shí)際類型
ResponseDTO<SysCryptDTO> responseDTO = JSONObject.parseObject(jsonString, new TypeReference<ResponseDTO<SysCryptDTO>>() {});
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容