- Android插件化開發(fā)指南
- 包建強(qiáng)
- 5725字
- 2019-01-05 08:31:11
第3章 反射
本章介紹Java中最強(qiáng)大的技術(shù):反射。
Java原生的反射語法艱澀難懂,于是我們一般將這些反射語法封裝成Utils類,包括反射類、反射方法(構(gòu)造函數(shù))、反射字段。這其中,做得最好的莫過于jOOR這個開源反射封裝庫。但是它有個缺點(diǎn),就是不適用于Android中定義為final的字段,就連作者也承認(rèn),jOOR只為了Java而設(shè)計(jì),而沒有考慮Android。
所有反射語法中最難的莫過于反射一個泛型類,而這又是我們在做插件化編程中不可避免的。
3.1 基本反射技術(shù)
反射包括以下技術(shù):
□ 根據(jù)一個字符串得到一個類的對象。
□ 獲取一個類的所有公用或私有、靜態(tài)或?qū)嵗淖侄巍⒎椒ā傩浴?/p>
□ 對泛型類的反射。
相比于其他語言,Java反射的語法是非常艱澀難懂的,我們按照上面的三點(diǎn)依次介紹。
提示
本節(jié)的示例代碼參見https://github.com/BaoBaoJianqiang/TestReflection。
3.1.1 根據(jù)一個字符串得到一個類
1.getClass
通過一個對象,獲取它的類型。類型用Class表示:
String str = “abc”; Class c1 = str.getClass();
2.Class.forName
這個方法用得最多。
通過一個字符串獲取一個類型。這個字符串由類的命名空間和類的名稱組成。而通過getSuperclass方法,獲取對象的父類類型:
try { Class c2 = Class.forName("java.lang.String"); Class c3 = Class.forName("android.widget.Button"); //通過getSuperClass,每個Class都有這個函數(shù) Class c5 = c3.getSuperclass(); //得到TextView } catch (ClassNotFoundException e) { e.printStackTrace(); }
3.class屬性
每個類都有class屬性,可以得到這個類的類型:
Class c6 = String.class; Class c7 = java.lang.String.class; Class c8 = MainActivity.InnerClass.class; Class c9 = int.class; Class c10 = int[].class;
4.TYPE屬性
基本類型,如BOOLEAN,都有TYPE屬性,可以得到這個基本類型的類型:
Class c11 = Boolean.TYPE; Class c12 = Byte.TYPE; Class c13 = Character.TYPE; Class c14 = Short.TYPE; Class c15 = Integer.TYPE; Class c16 = Long.TYPE; Class c17 = Float.TYPE; Class c18 = Double.TYPE; Class c19 = Void.TYPE;
看到這里,讀者也許會問,為什么不厭其煩地想要得到類或?qū)ο蟮念愋汀T诤竺娴恼鹿?jié),我們在使用Proxy.newProxyInstance()的時候,會大量用到這些類型值作為參數(shù)。
3.1.2 獲取類的成員
1.獲取類的構(gòu)造函數(shù)
獲取類的構(gòu)造函數(shù),包括private和public兩種,也支持無參數(shù)和有參數(shù)這兩種類型的構(gòu)造函數(shù)。
比如TestClassCtor這個類,就有很多構(gòu)造函數(shù):
public TestClassCtor() { name = "baobao"; } public TestClassCtor(int a) { } public TestClassCtor(int a, String b) { name = b; } private TestClassCtor(int a, double c) { }
1)獲取類的所有構(gòu)造函數(shù)。
通過Class的getDeclaredConstructors方法,獲取類的所有構(gòu)造函數(shù),包括public和private的構(gòu)造函數(shù),然后就可以通過for循環(huán)遍歷每一個構(gòu)造函數(shù)了:
TestClass r = new TestClass(); Class temp = r.getClass(); String className = temp.getName(); // 獲取指定類的類名 Log.v("baobao", "獲取類的所有ctor,不分public還是private---------------------"); //獲取類的所有ctor,不分public還是private try { Constructor[] theConstructors = temp.getDedaredConstructors(); // 獲取指定類的公有構(gòu)造方法 for (int i = 0; i < theConstructors.length; i++) { int mod = theConstructors[i].getModifiers(); // 輸出修飾域和方法名稱 Log.v("baobao", Modifier.toString(mod) + " " + className + "("); Class[] parameterTypes = theConstructors[i].getParameterTypes(); //獲取指定構(gòu)造方法參數(shù)的集合 for (int j = 0; j < parameterTypes.length; j++) { // 輸出打印參數(shù)列表 Log.v("baobao", parameterTypes[j].getName()); if (parameterTypes.length > j + 1) { Log.v("baobao", ", "); } } Log.v("baobao", ")"); } } catch (Exception e) { e.printStackTrace(); }
如果只想獲取類的所有public構(gòu)造函數(shù),就不能再使用Class的getConstructors方法了,而要使用getDeclaredConstructors方法。
2)獲取類的某個構(gòu)造函數(shù)。
獲取無參數(shù)的構(gòu)造函數(shù):
Constructor c1 = temp.getDeclaredConstructor();
獲取有一個參數(shù)的構(gòu)造函數(shù),參數(shù)類型int:
Class[] p2 = {int.class}; Constructor c2 = temp.getDeclaredConstructor(p2);
獲取有兩個參數(shù)的構(gòu)造函數(shù),參數(shù)類型依次是int和String:
Class[] p3 = {int.class, String.class}; Constructor c3 = temp.getDeclaredConstructor(p3);
反射到類的構(gòu)造函數(shù)很重要,這是下述流程中至關(guān)重要的一步:通過字符串反射出一個類,然后通過反射獲取到類的構(gòu)造函數(shù),執(zhí)行構(gòu)造函數(shù)就得到了類的實(shí)例。有了實(shí)例,就可以通過反射進(jìn)一步得到實(shí)例的所有字段和方法。
3)調(diào)用構(gòu)造函數(shù)。
接下來通過反射調(diào)用構(gòu)造函數(shù),得到類的實(shí)例,這要借助于Constructor的newInstance方法:
Class r = Class.forName("jianqiang.com.testreflection.TestClassCtor"); //含參 Class[] p3 = {int.class, String.class}; Constructor ctor = r.getDeclaredConstructor(p3); Object obj = ctor.newInstance(1, "bjq"); //無參 Constructor ctor2 = r.getDeclaredConstructor(); Object obj2 = ctor2.newInstance();
如果構(gòu)造函數(shù)是無參數(shù)的,那么可以直接使用Class的newInstance方法:
Class r = Class.forName("jianqiang.com.testreflection.TestClassCtor"); Object obj4 = r.newInstance();
2.獲取類的私有實(shí)例方法并調(diào)用它
在TestClassCtor中,有一個私有方法doSOmething:
private String doSOmething(String d) { Log.v("baobao", "TestClassCtor, doSOmething"); return "abcd"; }
想獲取這個私有方法并執(zhí)行它,要寫如下代碼:
Class r = Class.forName("jianqiang.com.testreflection.TestClassCtor"); Class[] p3 = {int.class, String.class}; Constructor ctor = r.getDeclaredConstructor(p3); Object obj = ctor.newInstance(1, "bjq"); //以下4句話,調(diào)用一個private方法 Class[] p4 = {String.class}; Method method = r.getDeclaredMethod("doSOmething", p4); //在指定類中獲取指定的方法 method.setAccessible(true); Object argList[] = {"jianqiang"}; //這里寫死,下面有個通用的函數(shù)getMethodParamObject Object result = method.invoke(obj, argList);
3.獲取類的靜態(tài)的私有方法并調(diào)用它
在TestClassCtor中,有一個靜態(tài)的私有方法work:
private static void work() { Log.v("baobao", "TestClassCtor, work"); }
想獲取這個靜態(tài)的私有方法并執(zhí)行它,要寫如下代碼:
Class r = Class.forName("jianqiang.com.testreflection.TestClassCtor"); //以下3句話,調(diào)用一個private靜態(tài)方法 Method method = r.getDeclaredMethod("work"); //在指定類中獲取指定的方法 method.setAccessible(true); method.invoke(null);
4.獲取類的私有實(shí)例字段并修改它
在TestClassCtor中,有一個私有的實(shí)例字段name:
public class TestClassCtor { private String name; public String getName() { return name; } }
想獲取這個私有實(shí)例字段并修改它的值,要寫如下代碼:
//以下4句話,創(chuàng)建一個對象 Class r = Class.forName("jianqiang.com.testreflection.TestClassCtor"); Class[] p3 = {int.class, String.class}; Constructor ctor = r.getDeclaredConstructor(p3); Object obj = ctor.newInstance(1, "bjq"); //獲取name字段,private Field field = r.getDeclaredField("name"); field.setAccessible(true); Object fieldObject = field.get(obj); //只對obj有效 field.set(obj, "jianqiang1982");
值得注意的是,這次修改僅對當(dāng)前這個對象有效,如果接下來我們再次創(chuàng)建一個TestClassCtor對象,它的name字段的值為空而不是jianqiang1982:
TestClassCtor testClassCtor = new TestClassCtor(100); testClassCtor.getName(); //仍然返回null,并沒有修改
5.獲取類的私有靜態(tài)字段并修改它
在TestClassCtor中,有一個靜態(tài)的私有字段address,想獲取這個私有的靜態(tài)字段并修改它的值,要寫如下代碼:
//以下4句話,創(chuàng)建一個對象 Class r = Class.forName("jianqiang.com.testreflection.TestClassCtor"); //獲取address靜態(tài)字段,private Field field = r.getDeclaredField("address"); field.setAccessible(true); Object fieldObject = field.get(null); field.set(fieldObject, "ABCD"); //靜態(tài)變量,一次修改,終生受用 TestClassCtor.printAddress();
與前面介紹的實(shí)例字段不同,靜態(tài)字段的值被修改了,下次再使用,這個字段的值是修改后的值。所謂“一次修改,終生受用”。
3.1.3 對泛型類的反射
Android系統(tǒng)源碼中存在大量泛型,所以插件化技術(shù)離不開對泛型進(jìn)行反射,比如單例模式(Singleton),下述代碼是從Android源碼中找出來的:
public abstract class Singleton<T> { private T mInstance; protected abstract T create(); public final T get() { synchronized (this) { if (mInstance == null) { mInstance = create(); } return mInstance; } } }
Singleton是一個泛型類,我們可以通過以下三行代碼,取出Singleton中的mInstance字段:
Class<? > singleton = Class.forName("jianqiang.com.testreflection.Singleton"); Field mInstanceField = singleton.getDeclaredField("mInstance"); mInstanceField.setAccessible(true);
同時,Singleton也是一個抽象類,在實(shí)例化Singleton的時候,一定要實(shí)現(xiàn)create這個抽象方法。
接下來我們看ActivityManagerNative(AMN)這個類,其中和Singleton有關(guān)的是下面幾行代碼:
public class AMN { private static final Singleton<ClassB2Interface> gDefault = new Singleton <ClassB2Interface>() { protected ClassB2Interface create() { ClassB2 b2 = new ClassB2(); b2.id = 2; return b2; } }; static public ClassB2Interface getDefault() { return gDefault.get(); } }
上面的代碼中g(shù)Default是AMN的靜態(tài)私有變量,它是Singleton類型的,所以要實(shí)現(xiàn)create方法,返回一個ClassB2類型的對象。
在Android的源碼中,可通過AMN.getDefault()來獲取create方法創(chuàng)建的ClassB2對象。
我們可以通過以下幾行代碼來獲取AMN的gDefault靜態(tài)私有字段,進(jìn)一步得到生成的ClassB2類型對象rawB2Object:
Class<? > activityManagerNativeClass = Class.forName("jianqiang.com.testreflection. AMN"); Field gDefaultField = activityManagerNativeClass.getDeclaredField("gDefault"); gDefaultField.setAccessible(true); Object gDefault = gDefaultField.get(null); // AMN的gDefault對象里面原始的 B2對象 Object rawB2Object = mInstanceField.get(gDefault);
后來我們可能發(fā)現(xiàn),rawB2Object不是我們所需要的,我們希望把它換成ClassB2Mock類型的對象proxy。
ClassB2Mock是對rawB2Object的動態(tài)代理,這里使用了Proxy.newProxyInstance方法,額外打印了一行日志:
// 創(chuàng)建一個這個對象的代理對象ClassB2Mock, 然后替換這個字段, 讓我們的代理對象幫忙干活 Class<? > classB2Interface = Class.forName("jianqiang.com.testreflection.ClassB2Interface"); Object proxy = Proxy.newProxyInstance( Thread.currentThread().getContextClassLoader(), new Class<? >[] { classB2Interface }, new ClassB2Mock(rawB2Object)); mInstanceField.set(gDefault, proxy);
最后一行代碼就是把AMN中的gDefault字段的mInstance字段,設(shè)置為代理對象proxy。
經(jīng)過Hook, AMN.getDefault().doSomething()將執(zhí)行ClassB2Mock里面的邏輯。
Android源碼中AMN的思路和我的這些代碼在思路上是一致的,只是這里用ClassB2和ClassB2Mock來模擬,比較簡單。
3.2 jOOR
上面例子的語法都是基于原始的Java語法,有沒有覺得這些語法很艱澀?我們希望用一種自然的、簡單的、面向?qū)ο蟮恼Z法,來取代上面這些艱澀的語言,于是便有了jOOR這個開源庫。
jOOR庫就兩個類,Reflect.java和ReflectException.java,所以我一般不依賴于gradle,而是直接把這兩個類拖到項(xiàng)目中來。
其中,Reflect.java最為重要,包括6個核心方法:
□ on:包裹一個類或者對象,表示在這個類或?qū)ο笊线M(jìn)行反射,類的值可以是Class,也可以是完整的類名(包含包名信息)。
□ create:用來調(diào)用之前的類的構(gòu)造方法,有兩種重載,一種有參數(shù),一種無參數(shù)。
□ call:方法調(diào)用,傳入方法名和參數(shù),如有返回值還需要調(diào)用get。
□ get:獲取(field和method返回)值相關(guān),會進(jìn)行類型轉(zhuǎn)換,常與call組合使用。
□ set:設(shè)置屬性值。
我們使用jOOR把上一節(jié)的代碼重構(gòu)一下。
提示
本節(jié)的示例代碼參見https://github.com/BaoBaoJianqiang/TestReflection2。
3.2.1 根據(jù)一個字符串得到一個類
1.getClass
通過一個字符串,獲取它的類型。類型用Class表示:
String str = "abc"; Class c1 = str.getClass();
這些代碼還是傳統(tǒng)的語法,并沒有改變。
2.根據(jù)字符串獲取一個類
對于jOOR,我們一般import它的Reflect.on方法,這樣我們就可以直接在代碼中使用on了,讓編碼更加簡單。代碼如下:
import static jianqiang.com.testreflection.joor.Reflect.on; //以下3個語法等效 Reflect r1 = on(Object.class); Reflect r2 = on("java.lang.Object"); Reflect r3 = on("java.lang.Object", ClassLoader.getSystemClassLoader()); //以下2個語法等效,實(shí)例化一個Object變量,得到Object.class Object o1 = on(Object.class).<Object>get(); Object o2 = on("java.lang.Object").get(); String j2 = on((Object)"abc").get(); int j3 = on(1).get(); //等價(jià)于Class.forName() try { Class j4 = on("android.widget.Button").type(); } catch (ReflectException e) { e.printStackTrace(); }
3.2.2 獲取類的成員
1.調(diào)用類的構(gòu)造函數(shù)
調(diào)用類的構(gòu)造函數(shù),包括private和public兩種,也支持無參數(shù)和有參數(shù)這兩種類型的構(gòu)造函數(shù)。
jOOR認(rèn)為構(gòu)造函數(shù)就是用來調(diào)用的,所以沒有給出獲取構(gòu)造函數(shù)的方法,而是直接給出了調(diào)用構(gòu)造函數(shù)的方法create:
TestClassCtor r = new TestClassCtor(); Class temp = r.getClass(); String className = temp.getName(); // 獲取指定類的類名 //public構(gòu)造函數(shù) Object obj = on(temp).create().get(); //無參 Object obj2 = on(temp).create(1, "abc").get(); //有參 //private構(gòu)造函數(shù) TestClassCtor obj3 = on(TestClassCtor.class).create(1, 1.1).get(); String a = obj3.getName();
2.獲取類的私有實(shí)例方法
獲取類的私有實(shí)例方法并調(diào)用它:
//以下4句話,創(chuàng)建一個對象 TestClassCtor r = new TestClassCtor(); Class temp = r.getClass(); Reflect reflect = on(temp).create(); //調(diào)用一個實(shí)例方法 String a1 = reflect.call("doSOmething", "param1").get();
3.獲取類的私有靜態(tài)方法
獲取類的私有靜態(tài)方法并調(diào)用它:
//以下4句話,創(chuàng)建一個對象 TestClassCtor r = new TestClassCtor(); Class temp = r.getClass(); Reflect reflect = on(temp).create(); //調(diào)用一個靜態(tài)方法 on(TestClassCtor.class).call("work").get();
4.獲取類的私有實(shí)例字段
獲取類的私有實(shí)例字段并修改它:
Reflect obj = on("jianqiang.com.testreflection.TestClassCtor").create(1, 1.1); obj.set("name", "jianqiang"); Object obj1 = obj.get("name");
5.獲取類的私有靜態(tài)字段
獲取類的私有靜態(tài)字段并修改它:
on("jianqiang.com.testreflection.TestClassCtor").set("address", "avcccc"); Object obj2 = on("jianqiang.com.testreflection.TestClassCtor").get("address");
3.2.3 對泛型類的反射
這個例子用jOOR寫起來會更簡單:
//獲取AMN的gDefault單例gDefault, gDefault是靜態(tài)的 Object gDefault = on("jianqiang.com.testreflection.AMN").get("gDefault"); // gDefault是一個 android.util.Singleton對象;我們?nèi)〕鲞@個單例里面的mInstance字段 // mInstance就是原始的ClassB2Interface對象 Object mInstance = on(gDefault).get("mInstance"); // 創(chuàng)建一個這個對象的代理對象ClassB2Mock, 然后替換這個字段, 讓我們的代理對象幫忙干活 Class<? > classB2Interface = on("jianqiang.com.testreflection.ClassB2Interface").type(); Object proxy = Proxy.newProxyInstance( Thread.currentThread().getContextClassLoader(), new Class<? >[] { classB2Interface }, new ClassB2Mock(mInstance)); on(gDefault).set("mInstance", proxy);
外界對jOOR的評價(jià)很高,我看了相關(guān)的文章,大多是一些浮于表面的介紹,然后就被媒體無限放大,殊不知jOOR在Android領(lǐng)域有個很大的缺陷,那就是不支持反射final類型的字段。
看一個例子,User類有兩個final字段,其中userId是靜態(tài)字段,name是實(shí)例字段:
public class User { private final static int userId = 3; private final String name = "baobao"; }
在使用jOOR反射時的語法如下:
//實(shí)例字段 Reflect obj = on("jianqiang.com.testreflection.User").create(); obj.set("name", "jianqiang"); Object newObj = obj.get("name"); //靜態(tài)字段 Reflect obj2 = on("jianqiang.com.testreflection.User"); obj2.set("userId", "123"); Object newObj2 = obj2.get("userId");
上面這段代碼在執(zhí)行set語法時必然報(bào)錯,拋出一個NoSuchFieldException異常。究其原因,是jOOR的Reflect的set方法會在遇到final時,嘗試反射出Field類的modifiers字段,在Java環(huán)境是有這個字段的,但是Android版本的Field類并沒有這個字段,于是就報(bào)錯了。
3.3 對基本反射語法的封裝
考慮到j(luò)OOR的局限性,我們不得不另辟蹊徑。對基本的Java反射語法進(jìn)行封裝,以得到簡單的語法。
考察前面介紹的種種語法,無論是反射出一個類,還是反射出一個構(gòu)造函數(shù)并調(diào)用它,都是為了進(jìn)一步讀寫類的方法和字段,所以我們只要封裝以下幾個方法即可:
□ 反射出一個構(gòu)造函數(shù)并調(diào)用它。
□ 調(diào)用靜態(tài)方法。
□ 調(diào)用實(shí)例方法。
□ 獲取和設(shè)置一個字段的值。
□ 對泛型的處理。
提示
本節(jié)的示例代碼參見https://github.com/BaoBaoJianqiang/TestReflection3。
3.3.1 反射出一個構(gòu)造函數(shù)
在RefInvoke類定義方法如下:
public static Object createObject(String className, Class[] pareTyples, Object[] pareVaules) { try { Class r = Class.forName(className); Constructor ctor = r.getDeclaredConstructor(pareTyples); ctor.setAccessible(true); return ctor.newInstance(pareVaules); } catch (Exception e) { e.printStackTrace(); } return null; }
以下是對這個封裝函數(shù)的調(diào)用:
Class r = Class.forName(className); //含參 Class[] p3 = {int.class, String.class}; Object[] v3 = {1, "bjq"}; Object obj = RefInvoke.createObject(className, p3, v3); //無參 Object obj2 = RefInvoke.createObject(className, null, null);
3.3.2 調(diào)用實(shí)例方法
在RefInvoke類定義方法如下:
public static Object invokeInstanceMethod(Object obj, String methodName, Class[] pareTyples, Object[] pareVaules) { if(obj == null) return null; try { //調(diào)用一個private方法 Method method = obj.getClass().getDeclaredMethod(methodName, pareTyples); //在指定類中獲取指定的方法 method.setAccessible(true); return method.invoke(obj, pareVaules); } catch (Exception e) { e.printStackTrace(); } return null; }
以下是調(diào)用這個封裝函數(shù):
Class[] p3 = {}; Object[] v3 = {}; RefInvoke.invokeStaticMethod(className, "work", p3, v3);
3.3.3 調(diào)用靜態(tài)方法
在RefInvoke類定義方法如下:
public static Object invokeStaticMethod(String className, String method_name, Class[] pareTyples, Object[] pareVaules) { try { Class obj_class = Class.forName(className); Method method = obj_class.getDeclaredMethod(method_name, pareTyples); method.setAccessible(true); return method.invoke(null, pareVaules); } catch (Exception e) { e.printStackTrace(); } return null; }
以下是調(diào)用這個封裝函數(shù):
Class[] p4 = {String.class}; Object[] v4 = {"jianqiang"}; Object result = RefInvoke.invokeInstanceMethod(obj, "doSOmething", p4, v4);
3.3.4 獲取并設(shè)置一個字段的值
在RefInvoke類定義方法如下:
public static Object getFieldObject(String className, Object obj, String filedName) { try { Class obj_class = Class.forName(className); Field field = obj_class.getDeclaredField(filedName); field.setAccessible(true); return field.get(obj); } catch (Exception e) { e.printStackTrace(); } return null; } public static void setFieldObject(String classname, Object obj, String filedName, Object filedVaule) { try { Class obj_class = Class.forName(classname); Field field = obj_class.getDeclaredField(filedName); field.setAccessible(true); field.set(obj, filedVaule); } catch (Exception e) { e.printStackTrace(); } }
以下是調(diào)用這兩個封裝函數(shù):
//獲取實(shí)例字段 Object fieldObject = RefInvoke.getFieldObject(className, obj, "name"); RefInvoke.setFieldObject(className, obj, "name", "jianqiang1982"); //獲取靜態(tài)字段 Object fieldObject = RefInvoke.getFieldObject(className, null, "address"); RefInvoke.setFieldObject(className, null, "address", "ABCD");
3.3.5 對泛型類的處理
借助于前面封裝的5個方法,我們重寫對泛型的反射調(diào)用:
//獲取AMN的gDefault單例gDefault, gDefault是靜態(tài)的 Object gDefault = RefInvoke.getFieldObject("jianqiang.com.testreflection.AMN", null, "gDefault"); // gDefault是一個 android.util.Singleton對象;我們?nèi)〕鲞@個單例里面的mInstance字段 Object rawB2Object = RefInvoke.getFieldObject( "jianqiang.com.testreflection.Singleton", gDefault, "mInstance"); // 創(chuàng)建一個這個對象的代理對象ClassB2Mock, 然后替換這個字段, 讓我們的代理對象幫忙干活 Class<? > classB2Interface = Class.forName("jianqiang.com.testreflection. ClassB2Interface"); Object proxy = Proxy.newProxyInstance( Thread.currentThread().getContextClassLoader(), new Class<? >[] { classB2Interface }, new ClassB2Mock(rawB2Object)); //把Singleton的mInstance替換為proxy RefInvoke.setFieldObject("jianqiang.com.testreflection.Singleton", gDefault, "mInstance", proxy);
雖然不是面向?qū)ο蟮恼Z法,但總要簡單得多。
3.4 對反射的進(jìn)一步封裝
我們在3.3節(jié)對反射語法進(jìn)行了簡單的封裝,但在實(shí)際的使用中,我們發(fā)現(xiàn),有的時候,有很多不方便的地方。在本節(jié)中會對此進(jìn)行優(yōu)化。
提示
本節(jié)示例代碼參考https://github.com/BaoBaoJianqiang/TestReflection4
1.對于無參數(shù)和只有一個參數(shù)的處理
通過反射調(diào)用方法或者構(gòu)造函數(shù)的時候,它們有時只需要一個參數(shù),有時根本不需要參數(shù),但我們每次都要按照多個參數(shù)的方式來編寫代碼,如下所示:
Class r = Class.forName(className); //含參 Class[] p3 = {int.class, String.class}; Object[] v3 = {1, "bjq"}; Object obj = RefInvoke.createObject(className, p3, v3); //無參 Object obj2 = RefInvoke.createObject(className, null, null);
我們希望把代碼寫的更簡單一些,比如說這樣:
//無參 public static Object createObject(String className) { Class[] pareTyples = new Class[]{}; Object[] pareVaules = new Object[]{}; try { Class r = Class.forName(className); return createObject(r, pareTyples, pareVaules); } catch (ClassNotFoundException e) { e.printStackTrace(); } return null; } //一個參數(shù) public static Object createObject(String className, Class pareTyple, Object pareVaule) { Class[] pareTyples = new Class[]{ pareTyple }; Object[] pareVaules = new Object[]{ pareVaule }; try { Class r = Class.forName(className); return createObject(r, pareTyples, pareVaules); } catch (ClassNotFoundException e) { e.printStackTrace(); } return null; } //多個參數(shù) public static Object createObject(String className, Class[] pareTyples, Object[] pareVaules) { try { Class r = Class.forName(className); return createObject(r, pareTyples, pareVaules); } catch (ClassNotFoundException e) { e.printStackTrace(); } return null; } //多個參數(shù) public static Object createObject(Class clazz, Class[] pareTyples, Object[] pareVaules) { try { Constructor ctor = clazz.getDeclaredConstructor(pareTyples); ctor.setAccessible(true); return ctor.newInstance(pareVaules); } catch (Exception e) { e.printStackTrace(); } return null; }
以此類推,構(gòu)造函數(shù)就是方法,我們封裝的invokeStaticMethod和invokeInstanceMethod方法也可以有這許多種重載方式。
2.字符串可以替換為Class
截止到現(xiàn)在,我們加載類的方式,都是通過字符串的方式,比如createObject的實(shí)現(xiàn):
public static Object createObject(String className, Class[] pareTyples, Object[] pareVaules) { try { Class r = Class.forName(className); Constructor ctor = r.getConstructor(pareTyples); return ctor.newInstance(pareVaules); } catch (Exception e) { e.printStackTrace(); } return null; }
但有時候,我們直接就擁有這個類的Class類型,而不再用Class.forName(className)的方式再去生成,于是就可以有字符串和Class這兩種形式的方法重載,如下所示:
//多個參數(shù) public static Object createObject(String className, Class[] pareTyples, Object[] pareVaules) { try { Class r = Class.forName(className); return createObject(r, pareTyples, pareVaules); } catch (ClassNotFoundException e) { e.printStackTrace(); } return null; } //多個參數(shù) public static Object createObject(Class clazz, Class[] pareTyples, Object[] pareVaules) { try { Constructor ctor = clazz.getConstructor(pareTyples); return ctor.newInstance(pareVaules); } catch (Exception e) { e.printStackTrace(); } return null; }
RefInvoke中的所有方法,大都擁有String和Class這兩種方法的重載,限于篇幅,這里就不一一介紹了,具體情況可以參考源碼例子。
3.區(qū)分靜態(tài)字段和實(shí)例字段
在反射字段的時候,我們發(fā)現(xiàn),對靜態(tài)字段和實(shí)例字段的處理,區(qū)別就在一個地方,由于靜態(tài)字段的反射不需要obj參數(shù),所以設(shè)置為null,如下所示:
//獲取實(shí)例字段 Object fieldObject = RefInvoke.getFieldObject(className, obj, "name"); RefInvoke.setFieldObject(className, obj, "name", "jianqiang1982"); //獲取靜態(tài)字段 Object fieldObject = RefInvoke.getFieldObject(className, null, "address"); RefInvoke.setFieldObject(className, null, "address", "ABCD");
為了區(qū)分靜態(tài)字段和實(shí)例字段這兩種場景,我們把靜態(tài)字段的讀寫方法改名為getStaticFieldObject和setStaticFieldObject,它們間接的調(diào)用了實(shí)例字段的getFieldObject和setFieldObject方法,但是省略了obj參數(shù),如下所示,
public static Object getStaticFieldObject(String className, String filedName) { return getFieldObject(className, null, filedName); } public static void setStaticFieldObject(String classname, String filedName, Object filedVaule) { setFieldObject(classname, null, filedName, filedVaule); }
那么在反射靜態(tài)字段的時候,就可以優(yōu)雅的寫出下列代碼了:
Object fieldObject = RefInvoke.getFieldObject(className, null, "address"); RefInvoke.setStaticFieldObject(className, "address", "ABCD");
4.對反射讀寫字段的優(yōu)化
繼續(xù)觀察對實(shí)例字段的封裝方法,以getFieldObject為例:
public static Object getFieldObject(Class clazz, Object obj, String filedName) { try { Field field = clazz.getDeclaredField(filedName); field.setAccessible(true); return field.get(obj); } catch (Exception e) { e.printStackTrace(); } return null; }
我們發(fā)現(xiàn),大多數(shù)情況下,obj的類型就是clazz,所以可以省略clazz參數(shù),于是編寫兩個簡易版的重載方法,如下所示:
public static Object getFieldObject(Object obj, String filedName) { return getFieldObject(obj.getClass(), obj, filedName); } public static void setFieldObject(Object obj, String filedName, Object filedVaule) { setFieldObject(obj.getClass(), obj, filedName, filedVaule); }
然后就可以優(yōu)雅的編寫代碼了:
Object fieldObject = RefInvoke.getFieldObject(className, "address"); RefInvoke.setStaticFieldObject(className, "address", "ABCD");
但是也有例外,如果obj的類型不是clazz(obj有可能是clazz的孫子,甚至輩分更低),那么就只能老老實(shí)實(shí)的使用原先封裝的getFieldObject和setFieldObject方法。
3.5 本章小結(jié)
本章給出了反射語法的三種編寫方式:
1)基本反射語法。
2)jOOR語法。
3)對基本反射語法的封裝。
由于jOOR對Android的支持不是很好,所以業(yè)內(nèi)開源框架一般采用的是方式1和3。
在本書后面的章節(jié)中,使用方式3來編寫插件化的反射代碼。
- 一線架構(gòu)師實(shí)踐指南
- 鑄魂:軟件定義制造
- 軟件研發(fā)效能提升之美
- 手機(jī)軟件測試最佳實(shí)踐
- 實(shí)用軟件工程
- 軟件架構(gòu)的藝術(shù)
- 移動終端應(yīng)用軟件開發(fā)實(shí)戰(zhàn)
- 鋒利的jQuery
- Android驅(qū)動開發(fā)與移植實(shí)戰(zhàn)詳解
- React Cookbook中文版:87個案例帶你精通React框架
- 自然語言理解與行業(yè)知識圖譜:概念、方法與工程落地
- DDD工程實(shí)戰(zhàn):從零構(gòu)建企業(yè)級DDD應(yīng)用
- 云原生Kubernetes全棧架構(gòu)師實(shí)戰(zhàn)
- 云原生應(yīng)用開發(fā)實(shí)戰(zhàn):基于.NET開發(fā)框架及Kubernetes容器編排技術(shù)
- 點(diǎn)云配準(zhǔn)從入門到精通