前言
今天Android移動端要加個新功能,所以回歸Android程序員的身份.開發的過程中,發現了之前的代碼寫的有很多問題,真的應該把時間抽出來重構一下了.
其中有反射的一個坑,工具類某方法反射獲取傳入Model的屬性值.但是當我把公共屬性抽出來做基類的時候,發現獲取不到基類的屬性值了.原因是使用了getDeclaredFields();
分析
方法 | 功能 |
---|---|
getFields() | 獲取所有public字段,包括父類字段 |
getDeclaredFields() | 獲取所有字段,public和protected和private,但是不包括父類字段 |
寫個小方法驗證一下下~
寫兩個類,里面定義三個字段,分別用public,protected,private修飾,
一個叫ParentModel,作為父類.
一個叫model,繼承ParentModel
/** * 用作父類 */ public class ParentModel { private String p_privateField; public String p_publicField; protected String p_protectedField; }
/** * 子類,繼承上面定義的用作父類的ParentModel */ public class Model extends ParentModel{ private String privateField; public String publicField; protected String protectedField; }
ok,分別使用getFields()和getDeclaredFields()獲取model的字段,循環打印出來.
Field[] fs = Model.class.getFields(); Field[] fs1 = Model.class.getDeclaredFields(); for (Field f:fs) { Log.d("getFields","getFields---"+f.getName()); } for (Field f:fs1) { Log.d("getDeclaredFields","getDeclaredFields---"+f.getName()); }
見證答案的時候到了~
getFields()的打印輸出:
getDeclaredFields()的打印輸出:
測試證實了我們上面的結論是對的.
我想獲取子類和父類的所有Field
如果想用反射通過Model獲取parentModel和Model的所有字段,怎么辦?很明顯上面的兩個方法都是滿足不了的.那怎么辦?
不用怕,我們遞歸Model的父類去getDeclaredFields(),代碼如下:
List<Field> fieldList = new ArrayList<>() ; Class tempClass = Model.class; while (tempClass != null) {//當父類為null的時候說明到達了最上層的父類(Object類). fieldList.addAll(Arrays.asList(tempClass .getDeclaredFields())); tempClass = tempClass.getSuperclass(); //得到父類,然后賦給自己 } for (Field f : fieldList) { Log.d("getAllFields","getFields---"+f.getName()); }
可以看到我們獲取了Model和ParentModel的全部字段,不僅如此,還多出來了兩個字段shadow$_klass_
和shadow_monitor_
,這個是Object中的字段.
shadow$_monitor_
和shadow$_klass_
是Android sdk21之后Object增加的兩個字段。
如果你想屏蔽Object類的影響,可以為while循環再添加一個條件:
while (tmpClass !=null && !tmpClass.getName().toLowerCase().equals("java.lang.object") ) { .... }
更新說明
2017.6.27更新:
之前被網友 lucky_god88 指出博客反射獲取的值和真實情況不符,核實之后,已經更正為正確答案,這里謝謝可愛的lucky_god88 發現並給我指出問題,解決問題的同時自己也在成長。同時也反省自己,以后要代碼多加驗證,謹慎細致,認真負責。
問題:
1.getFields() 獲取到 protected 類型字段的值
這個原因至今沒有再次重現,很奇怪,很費解
2.getFields() 和 getDeclaredFields() 方法反射獲取多了一個字段$change
這個和開發工具的配置有關系,好像是因為開啟了Instant run 造成的,而且Android Studio 2.2.3已經修復了,鏈接在這里
參考資料
Retrieving the inherited attribute names/values using Java Reflection