關於集合帶泛型的反射

我看到過有這麼幾個關於集合反射的問題,今天再次試驗了一下,也查看了相關的文檔,有點小心得,和大家分享一下。
先看個例子吧:
一共有三個:

第一個是限定爲Integer類型:

package cn.conpany.test.reflect;

import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Collection;

        public class RefectColl {
            public static void main(String[] args) throws Exception {
                    
                    //定義爲Integer類型的集合,此時只能加入Integer類型
                    ArrayList<Integer> list = new ArrayList<Integer>();
                    list.add(0);
                    
                    //加入其它類型,需要通過反射跳過編譯器,必須用Object,這裏會記錄下類型
                list.getClass().getMethod("add", Object.class).invoke(list, "ssss");
                System.out.println("size:" + list.size());//2
                
                //驗證get方法返回類型,只要是返回的String類型,都需強轉
                System.out.println("get(0)返回類型:" + list.get(0).getClass().getName());//java.lang.Integer--->原始類型是Integer集合
                System.out.println("get(1)返回類型:" + ((Object)list.get(1)).getClass().getName());//java.lang.String
                
                //集合原始類型爲Integer類型集合,無需強轉爲Object類,可直接獲取
                System.out.println("get(0)值:" + list.get(0));//0
                System.out.println("get(1)值:" + list.get(1));//ssss
            }
        }

第二種爲String類型:
package cn.conpany.test.reflect;

import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Collection;

public class RefectColl2 {
    public static void main(String[] args) throws Exception {
            
            //定義爲String類型的集合,此時只能加入String類型
            ArrayList<String> list = new ArrayList<String>();
            list.add("abc");
            
            
            
            //加入其它類型,需要通過反射跳過編譯器,必須用Object,這裏會記錄下類型
            list.getClass().getMethod("add", Object.class).invoke(list, new Integer(123));
            list.getClass().getMethod("add", Object.class).invoke(list, 124);
            list.getClass().getMethod("add", Object.class).invoke(list, new Persont("ZS",20));

            //這個size方法與集合定義的類型無關
            System.out.println("size:" + list.size());//4
            
            //驗證get方法返回的類型,只要是返回的String類型,都需強轉
            System.out.println("get(0)返回類型:" + list.get(0).getClass().getName());//get(0)返回類型:java.lang.String--->原始類型是String集合
            System.out.println("get(1)返回類型:" + ((Object)list.get(1)).getClass().getName());//get(1)返回類型:java.lang.Integer
            System.out.println("get(2)返回類型:" + ((Object)list.get(2)).getClass().getName());//get(2)返回類型:java.lang.Integer
            System.out.println("get(3)返回類型:" + ((Object)list.get(3)).getClass().getName());//get(3)返回類型:cn.conpany.test.reflect.Persont
            
            //可直接獲取編譯器前的原類型,即String類型
            System.out.println("get(0)值:" + list.get(0));//abc
            //集合的原始類型定義爲String集合類型,所以,需要先強轉爲Object類,才能獲取,否則編譯失敗
            System.out.println("get(1)值:" + (Object)list.get(1));//123
        System.out.println("get(2)值:" + (Object)list.get(2));//124
        System.out.println("get(3)值:" + (Object)list.get(3));//name:ZS,age:20
    }
}
第三種爲Persont類型

//定義Persont類
class Persont{
        private String name;
        private int age;
        public Persont(String name, int age) {
                this.name = name;
                this.age = age;
        }
                
                public String toString(){
                        return "name:" + name + ",age:" + age;
                }
        }

package cn.conpany.test.reflect;

import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Collection;

public class RefectColl3 {
    public static void main(String[] args) throws Exception {
            
            //定義爲String類型的集合,此時只能加入String類型
            ArrayList<Persont> list = new ArrayList<Persont>();
            list.add(new Persont("LS",27));
            
            
            //加入其它類型,需要通過反射跳過編譯器,必須用Object,這裏會記錄下類型
            list.getClass().getMethod("add", Object.class).invoke(list, new Integer(123));
            list.getClass().getMethod("add", Object.class).invoke(list, 124);
            list.getClass().getMethod("add", Object.class).invoke(list, new Persont("ZS",20));

            //這個size方法與集合定義的類型無關
            System.out.println(list.size());//4
            
            //驗證get方法返回的類型,只要是返回的String類型,都需強轉
            System.out.println("get(0)返回類型:" + list.get(0).getClass().getName());//cn.conpany.test.reflect.Persont--->原始類型是Persont集合
            System.out.println("get(1)返回類型:" + ((Object)list.get(1)).getClass().getName());//java.lang.Integer
            System.out.println("get(2)返回類型:" + ((Object)list.get(2)).getClass().getName());//java.lang.Integer
            System.out.println("get(3)返回類型:" + ((Object)list.get(3)).getClass().getName());//cn.conpany.test.reflect.Persont
            
            //可直接獲取跳過編譯器前的原類型,即String類型
            System.out.println("get(0)值:" + list.get(0));//name:LS,age:27
            //集合的原始類型定義的是Persont集合類型,無需強轉,直接獲取
            System.out.println("get(1)值:" + list.get(1));//123
        System.out.println("get(2):" + list.get(2));//124
        System.out.println("get(3):" + list.get(3));//name:ZS,age:20
    }
}
結論:
代碼很多,但是你認真看了,就會發現一個特別有趣的現象:
1、查看ArrayList的API文檔,你就可以發現,其中的ArrayList是這樣定義的:ArrayList<E>,而其中的add方法定義爲:boolean add(E e),你是不是發現了什麼,沒錯,即使反射將類型擦出了,其實反射是脆弱的,即使擦出了集合的類型信息,但是其中的方法可能還會保留原始限定的類型,所以,在加入的時候,仍需要用Object.class。
2、對於返回類型與集合無法的,如size這個方法,無需強轉,直接獲取。
3、很重要的一點:除了String類型以外的其他類型(包括自定義類型),無需強轉,可以直接使用相應集合中的方法。如上面的get()方法。我個人理解,與集合有關的方法,如get方法只有在你添加進元素時,才能調用,所以,即使get定義爲:E get(int index),也無需強轉。這裏,String類型除外。
4、但是對於String類型呢?作爲String類型的值,如果添加入限定爲非String集合中,那麼就需要強轉,究其原因,我只能說String是比較特殊的一個類,它有如下特性:被定義爲final,而且其值是存在了常量池中,並且可以將任意類型值通過字符串打印。基於如此特殊的類,那麼,它必然也有某些特性。這裏,我會繼續探究,如果誰有個人見解,請多指教,謝謝

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章