干复旬!貨遏餐!反射封裝給你直接拿去用

發(fā)現(xiàn)已經(jīng)有一個(gè)月左右沒(méi)更新文章了,好吧赢底,真的沒(méi)什么好寫的失都,一直在努力工作工作工作柏蘑。突然發(fā)現(xiàn)以前好像沒(méi)寫過(guò)反射的文章,終于可以偷雞一回了粹庞,這次咳焚,純伸手黨福利好嗎,封裝反射工具類庞溜,想用直接拿去用(項(xiàng)目地址在最下方)革半。

一. 反射調(diào)用

我今天找Api的時(shí)候發(fā)現(xiàn)一篇寫得挺好的文章,http://www.reibang.com/p/9be58ee20dee
這位大佬把大部分常用的方法都列舉出來(lái)了流码,我就不重復(fù)寫了又官。
并且這篇不打算寫原理,純寫調(diào)用漫试。

二. 封裝

1.獲取class對(duì)象

在反射中六敬,最主要的就是class對(duì)象,無(wú)論你想反射獲取參數(shù)驾荣,還是調(diào)用方法外构,都需要先獲取到class對(duì)象,獲取class對(duì)象的方法很多播掷,我這里只寫了根據(jù)name來(lái)獲取

    public static Class getClass(String clsName){
        try {
            if (!TextUtils.isEmpty(clsName)){
                Class<?> cls = Class.forName(clsName);
                return cls;
            }
        }catch (Exception e){
            e.printStackTrace();
        }
        return null;
    }

還有一點(diǎn)审编,為什么獲取Class 對(duì)象要單獨(dú)拿出來(lái)做一個(gè)方法,因?yàn)橛幸恍┨厥獾膱?chǎng)景不一定能用類名來(lái)獲取到Class 對(duì)象歧匈,比如一些插件化框架垒酬,有提供自己的獲取Class 對(duì)象的方法,所以需要單獨(dú)封裝成一個(gè)方法件炉。

2.調(diào)用構(gòu)造方法

封裝調(diào)用構(gòu)造方法伤溉,主要用到Constructor

    public static Object getConstructor(Class cls, Class[] paramsType, Object[] params){
        Object result = null;
        try {
            if (cls != null){
                Constructor constructor = cls.getConstructor(paramsType);
                result = constructor.newInstance(params);
            }
        }catch (Exception e){
            e.printStackTrace();
        }
        return result;
    }

可以簡(jiǎn)單看看getConstructor和newInstance源碼

    public Constructor<T> getConstructor(Class<?>... parameterTypes)
        throws NoSuchMethodException, SecurityException {
        return getConstructor0(parameterTypes, Member.PUBLIC);
    }
    public T newInstance(Object ... initargs)
        throws InstantiationException, IllegalAccessException,
               IllegalArgumentException, InvocationTargetException
    {
        if (serializationClass == null) {
            return newInstance0(initargs);
        } else {
            return (T) newInstanceFromSerialization(serializationCtor, serializationClass);
        }
    }

一個(gè)是傳Class不定參數(shù),另一個(gè)是傳Object不定參數(shù)妻率,所以我這邊封裝的時(shí)候定義了兩個(gè)數(shù)組參數(shù)乱顾。
調(diào)用的時(shí)候

            Class[] clsA = new Class[]{String.class};
            Object[] objA = new Object[]{"參數(shù)1"};
            BaseReflectUtils.getConstructor(cls, clsA, objA);

其實(shí)調(diào)用無(wú)參的構(gòu)造方法也可以直接使用newInstance方法

cls.newInstance()
3.獲取參數(shù)

反射獲取參數(shù),也就是調(diào)用靜態(tài)變量宫静,用到Field
可能網(wǎng)上一般會(huì)這樣寫

Field daf = cls.getField(fname);
result = daf.get(null);

但是如果你直接這樣使用的話走净,你會(huì)發(fā)現(xiàn),靜態(tài)參數(shù)能正常調(diào)用孤里,非靜態(tài)參數(shù)你就拿不到值了伏伯,如果稍微了解一點(diǎn)原理的話就知道為什么了。get方法傳的參數(shù)是對(duì)象捌袜,哪個(gè)對(duì)象的參數(shù)说搅,靜態(tài)參數(shù)不用對(duì)象,所以可以直接傳null虏等。那么可以這樣寫

    public static Object getField(Class cls, String fname){
        return getField(cls, fname, null);
    }

    public static Object getField(Class cls, String fname, Object obj){
        Object result = null;
        try {
            Field daf = cls.getField(fname);
            // 反射靜態(tài)參數(shù)直接傳空就行弄唧,反射非靜態(tài)參數(shù)要使用相對(duì)應(yīng)的對(duì)象
            result = daf.get(obj);
        }catch (Exception e){
            e.printStackTrace();
        }
        return result;
    }

靜態(tài)參數(shù)就調(diào)第一個(gè)方法适肠,非靜態(tài)就調(diào)下面的并傳入對(duì)象。

4.調(diào)用方法

調(diào)用方法用的是Method
一般寫

            Method method = cls.getMethod(mname, paramsType);
            object = method.invoke(obj, params);

但是要考慮靜態(tài)和非靜態(tài)的情況候引,靜態(tài)和非靜態(tài)其實(shí)就是像上面一樣區(qū)別在于是否需要對(duì)象侯养。還要考慮是否要傳參數(shù),也是和上面一樣澄干,區(qū)別在于是不是會(huì)多出兩個(gè)參數(shù)逛揩,所以我這邊這樣寫。

    // 靜態(tài)無(wú)參
    public static Object getMethod(Class cls, String mname){
        return getMethod(cls, mname, null, null, null);
    }

    // 靜態(tài)有參
    public static Object getMethod(Class cls, String mname, Class[] paramsType, Object[] params){
        return getMethod(cls, mname, paramsType, params, null);
    }

    // 非靜態(tài)無(wú)參
    public static Object getMethod(Class cls, String mname, Object obj){
        return getMethod(cls, mname, null, null, obj);
    }

    public static Object getMethod(Class cls, String mname, Class[] paramsType, Object[] params, Object obj){
        Object object = null;
        try {
            Method method = cls.getMethod(mname, paramsType);
            object = method.invoke(obj, params);
        }catch (Exception e){
            e.printStackTrace();
        }
        return object;
    }

我就只舉個(gè)栗子麸俘,非靜態(tài)有參數(shù)的情況就這樣調(diào)用

Class cls = BaseReflectUtils.getClass("com.kylim.fstest.TestD");
Object obj = BaseReflectUtils.getConstructor(cls,new Class[0], new Object[0]);
Class[] clsA = new Class[]{int.class};
Object[] objA = new Object[]{101};
String result = (String) BaseReflectUtils.getMethod(cls, "testTwo", clsA, objA, obj);

會(huì)發(fā)現(xiàn)傳的參數(shù)有那么一點(diǎn)多辩稽,如果你覺(jué)得不好看,可以封裝個(gè)鏈?zhǔn)秸{(diào)用

    public static class Builder{

        private Class cls;
        private String mname;
        private Class[] paramsType;
        private Object[] params;
        private Object obj;

        public Builder setCls(Class cls) {
            this.cls = cls;
            return this;
        }

        public Builder setMname(String mname) {
            this.mname = mname;
            return this;
        }

        public Builder setParamsType(Class[] paramsType) {
            this.paramsType = paramsType;
            return this;
        }

        public Builder setParams(Object[] params) {
            this.params = params;
            return this;
        }

        public Builder setObj(Object obj) {
            this.obj = obj;
            return this;
        }

        public Object build(){
            // 類名和方法名不能為空
            if (cls == null || TextUtils.isEmpty(mname)){
                return null;
            }

            return getMethod(cls, mname, paramsType, params, obj);
        }

    }

這樣調(diào)用的時(shí)候

            String result = (String) new BaseReflectUtils.Builder().setCls(cls)
                    .setMname("testTwo")
                    .setParamsType(new Class[]{int.class})
                    .setParams(new Object[]{101})
                    .setObj(BaseReflectUtils.getConstructor(cls,new Class[0], new Object[0]))
                    .build();

確實(shí)參數(shù)會(huì)有點(diǎn)多从媚,會(huì)讓人有一種封不封裝沒(méi)啥區(qū)別逞泄,首先封裝的話有利于之后的擴(kuò)展,其次静檬,你可以對(duì)常用的反射(固定的名字或參數(shù)類型這些)就行二次封裝。

5. 調(diào)用子類

上面已經(jīng)講了最基礎(chǔ)的調(diào)用參數(shù)和方法兩種并级,下面就相當(dāng)于擴(kuò)展拂檩,如果存在子類的情況呢,要如何進(jìn)行封裝嘲碧。
在反射中獲取子類需要用到getClasses方法
可以這樣寫

    public static Class getChildClass(String clsName, String childClsName){
        try {
            if (!TextUtils.isEmpty(clsName)){
                Class<?> cls = Class.forName(clsName);
                Class[] children = cls.getClasses();
                if (children != null && !TextUtils.isEmpty(childClsName)){
                    for (int i = 0; i < children.length; i++) {
                        if (childClsName.equals(children[i].getSimpleName())){
                            return children[i];
                        }
                    }
                }
            }
        }catch (Exception e){
            e.printStackTrace();
        }
        return null;
    }

在外部可以調(diào)用

 Class cls = KlReflectUtils.getChildClass("com.kylim.fstest.TestE", "ChildTestE");

這樣就能獲取到對(duì)應(yīng)的Class 對(duì)象稻励,有了Class 對(duì)象之后,你想怎么反射就可以參照上邊愈涩。

6. 單例

平時(shí)寫代碼時(shí)也會(huì)用到單例望抽,反射寫得不多的話,可能一碰到單例就有點(diǎn)蒙了履婉。
其實(shí)單例也是獲取到對(duì)象然后調(diào)用對(duì)象的方法煤篙。
假設(shè)我寫這樣的一個(gè)單例

public class TestF {

    private static volatile TestF testF;

    public static TestF getInstance() {
        if (testF == null) {
            synchronized (TestF.class) {
                if (testF == null) {
                    testF = new TestF();
                }
            }
        }
        return testF;
    }

    public void testOne(){
        Log.v("testOne", "調(diào)用testOne");
    }

}

平時(shí)直接引用的情況是這樣寫

TestF.getInstance().testOne()

在反射中結(jié)合我們上面的封裝,我們可以這樣寫

            Class cls = BaseReflectUtils.getClass("com.kylim.fstest.TestF");
            BaseReflectUtils.getMethod(cls, "testOne", BaseReflectUtils.getMethod(cls, "getInstance"));

BaseReflectUtils.getMethod(cls, "getInstance")就是對(duì)象

7.Builder模式

最后說(shuō)一個(gè)特殊的栗子毁腿,Builder模式有時(shí)也會(huì)常用辑奈,比如參數(shù)多的時(shí)候,這樣對(duì)Builder模式的對(duì)象進(jìn)行反射的話就有點(diǎn)蛋疼已烤,我們舉個(gè)栗子鸠窗。
假如我寫個(gè)Builder類

public class TestG {

    private TestG(){

    }

    private TestG(Builder builder){
        Log.v("TestG", "結(jié)果:"+builder.code+"  "+builder.msg);
    }

    public static class Builder{

        private int code;
        private String msg;

        public Builder setCode(int code) {
            this.code = code;
            return this;
        }

        public Builder setMsg(String msg) {
            this.msg = msg;
            return this;
        }

        public TestG build(){
            return new TestG(this);
        }

    }

}

這個(gè)Demo里面假設(shè)有兩個(gè)參數(shù),實(shí)際上肯定不止兩個(gè)胯究,因?yàn)閮蓚€(gè)就沒(méi)必要用Builder稍计。
那么,我們寫反射的時(shí)候需要這樣寫

Class cls = KlReflectUtils.getChildClass("com.kylim.fstest.TestG", "Builder");
// 構(gòu)造方法
Object obj = BaseReflectUtils.getConstructor(cls, new Class[0], new Object[0]);

// 調(diào)用第一個(gè)方法
Class[] clsA = new Class[]{int.class};
Object[] objA = new Object[]{200};
Object obj1 =  BaseReflectUtils.getMethod(cls, "setCode", clsA, objA, obj);
// 調(diào)用第二個(gè)方法
Class[] clsB = new Class[]{String.class};
Object[] objB = new Object[]{"請(qǐng)求成功"};
Object obj2 =  BaseReflectUtils.getMethod(cls, "setMsg", clsB, objB, obj1);
// 調(diào)用第三個(gè)方法
BaseReflectUtils.getMethod(cls, "build", obj2);

可以看到才2個(gè)參數(shù)就需要寫這么多代碼裕循,參數(shù)多的話到時(shí)候改其中一個(gè)臣嚣,找到都煩了净刮,所以需要封裝一下。

    /**
     *  Builder模式的反射
     */
    public static class Builder{

        private Class cls;
        private Object object;

        public Builder(Class cls, Object obj){
            this.cls = cls;
            this.object = obj;
        }

        public Builder setBuilderData(String mname, Class[] paramsType, Object[] params){
            object = BaseReflectUtils.getMethod(cls, mname, paramsType, params, object);
            return this;
        }

        public Object build(){
            return build(null, null);
        }

        public Object build(Class[] paramsType, Object[] params){
            return BaseReflectUtils.getMethod(cls, "build", paramsType, params, object);
        }

    }

我這里用Builder來(lái)封裝茧球,每個(gè)人都可以根據(jù)自己的喜好來(lái)做
然后我們調(diào)用的時(shí)候庭瑰,就可以寫成這樣

        new KlReflectUtils.Builder(cls, obj)
                .setBuilderData( "setCode", new Class[]{int.class}, new Object[]{200})
                .setBuilderData( "setMsg", new Class[]{String.class}, new Object[]{"請(qǐng)求成功"})
                .build();

如果有10個(gè)參數(shù)的話就這樣

        new KlReflectUtils.Builder(cls, obj)
                .setBuilderData( "setCode", new Class[]{int.class}, new Object[]{200})
                .setBuilderData( "setMsg", new Class[]{String.class}, new Object[]{"請(qǐng)求成功"})
                .setBuilderData( .......)
                .setBuilderData( .......)
                .setBuilderData( .......)
                .setBuilderData( .......)
                .setBuilderData( .......)
                .setBuilderData( .......)
                .setBuilderData( .......)
                .setBuilderData( .......)
                .build();

三. 代碼地址

直接上地址,想用的可以直接去拿來(lái)用抢埋,主要是BaseReflectUtils和KlReflectUtils弹灭,BaseReflectUtils是基本的反射的封裝,KlReflectUtils是對(duì)常用的進(jìn)行二次封裝揪垄,其實(shí)代碼并不多穷吮。

https://github.com/994866755/handsomeYe.reflectDemo/tree/master

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市饥努,隨后出現(xiàn)的幾起案子捡鱼,更是在濱河造成了極大的恐慌,老刑警劉巖酷愧,帶你破解...
    沈念sama閱讀 216,402評(píng)論 6 499
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件驾诈,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡溶浴,警方通過(guò)查閱死者的電腦和手機(jī)乍迄,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,377評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)士败,“玉大人闯两,你說(shuō)我怎么就攤上這事×陆” “怎么了漾狼?”我有些...
    開封第一講書人閱讀 162,483評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)饥臂。 經(jīng)常有香客問(wèn)我逊躁,道長(zhǎng),這世上最難降的妖魔是什么隅熙? 我笑而不...
    開封第一講書人閱讀 58,165評(píng)論 1 292
  • 正文 為了忘掉前任志衣,我火速辦了婚禮,結(jié)果婚禮上猛们,老公的妹妹穿的比我還像新娘念脯。我一直安慰自己,他們只是感情好弯淘,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,176評(píng)論 6 388
  • 文/花漫 我一把揭開白布绿店。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪假勿。 梳的紋絲不亂的頭發(fā)上借嗽,一...
    開封第一講書人閱讀 51,146評(píng)論 1 297
  • 那天,我揣著相機(jī)與錄音转培,去河邊找鬼恶导。 笑死,一個(gè)胖子當(dāng)著我的面吹牛浸须,可吹牛的內(nèi)容都是我干的惨寿。 我是一名探鬼主播,決...
    沈念sama閱讀 40,032評(píng)論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼删窒,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼裂垦!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起肌索,我...
    開封第一講書人閱讀 38,896評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤蕉拢,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后诚亚,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體晕换,經(jīng)...
    沈念sama閱讀 45,311評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,536評(píng)論 2 332
  • 正文 我和宋清朗相戀三年站宗,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了闸准。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,696評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡份乒,死狀恐怖恕汇,靈堂內(nèi)的尸體忽然破棺而出腕唧,到底是詐尸還是另有隱情或辖,我是刑警寧澤,帶...
    沈念sama閱讀 35,413評(píng)論 5 343
  • 正文 年R本政府宣布枣接,位于F島的核電站颂暇,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏但惶。R本人自食惡果不足惜耳鸯,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,008評(píng)論 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望膀曾。 院中可真熱鬧县爬,春花似錦、人聲如沸添谊。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至忿檩,卻和暖如春则奥,著一層夾襖步出監(jiān)牢的瞬間著蛙,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,815評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工概荷, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人碌燕。 一個(gè)月前我還...
    沈念sama閱讀 47,698評(píng)論 2 368
  • 正文 我出身青樓误证,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親陆蟆。 傳聞我的和親對(duì)象是個(gè)殘疾皇子雷厂,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,592評(píng)論 2 353