今天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()的打印输出:
测试证实了我们上面的结论是对的.
如果想用反射通过Model获取parentModel和Model的所有字段,怎么办?很明显上面的两个方法都是满足不了的.那怎么办?
不用怕,我们递归Model的父类去getDeclaredFields(),代码如下:
List 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