詳解Java泛型之十分鐘理解泛型擦除
今天我們來講解泛型中另一個重要知識點——泛型擦除!
泛型擦除概念
泛型信息只存在于代碼編譯階段,但是在java的運行期(已經(jīng)生成字節(jié)碼文件后)與泛型相關(guān)的信息會被擦除掉,專業(yè)術(shù)語叫做類型擦除。我們來看一個例子:
- ArrayList<Integer> l1 = new ArrayList();
- ArrayList<String> l2 = new ArrayList();
- System.out.println(l1.getClass()==l2.getClass());
運行代碼,結(jié)果為True
這是因為ArrayList <String>
通過下面的例子我們做進一步的分析
- import java.lang.reflect.Field;
- public class GeneErasure<T> {
- T object;
- public GeneErasure(T object) {
- this.object = object;
- }
- public static void main(String[] args) {
- GeneErasure demo = new GeneErasure<String>("hi");
- Class classz = demo.getClass();
- System.out.println(classz.getName());
- //輸出com.my.generic.GeneErasure
- Field[] fs = classz.getDeclaredFields();
- for ( Field f:fs) {
- System.out.println("feild: "+f.getName()+"type:"+f.getType().getName());
- //輸出feild: object type:java.lang.Object
- }
- }
通過這個例子我們可以看到Class 的類型仍然是GeneErasure并不是GeneErasure <T>
輸出結(jié)果為:
- feild: object type:java.lang.String
所以,在泛型類被類型擦除的時候,之前泛型類中的類型參數(shù)部分如果沒有指定上限,如<T>
利用類型擦除干“壞事兒”
大家都知道,下面這段代碼l.add(123)無法編譯通過,因為123不是String類型,這也是使用泛型的好處之一。
- ArrayList<String> l=new ArrayList<String>();
- l.add("abc");
- l.add(123);
但是我們理解了泛型擦除的原理,我們可以巧妙地利用這個原理結(jié)合反射知識干一些“壞事”,例如:
- ArrayList<String>l=new ArrayList<String>();
- l.add("abc");
- try {
- Method method = l.getClass().getDeclaredMethod("add",Object.class);
- method.invoke(l,"test");
- method.invoke(l,100.f);
- }catch (Exception e) {
- e.printStackTrace();
- }
- System.out.println("list的大小是:"+l.size());
- for ( Object o: l){
- System.out.println(o);
- }
運行結(jié)果是:
- list的大小是:3
- abc
- test
- 100.0(被成功插入到ArrayList中)
我們可以看見100.0 成功地插入到ArrayList
通俗的理解
我們可以將泛型比作是一個看守,他來守護我們的代碼安全,然后設(shè)置各項規(guī)定,“xxx 禁止出入”的提醒。而現(xiàn)實生活中,也總會有些人能夠基于對門衛(wèi)們生活作息的規(guī)律,繞開他們的監(jiān)視(反射結(jié)合泛型擦除)來干一些壞事兒 。