在使用 Java 反射时,我对 getFields
方法和 getDeclaredFields
方法之间的区别感到有些困惑。
我读到 getDeclaredFields
允许您访问类的所有字段,而 getFields
只返回公共字段。如果是这种情况,为什么不总是使用 getDeclaredFields
?
有人可以详细说明一下,并解释这两种方法之间的区别,以及何时/为什么要使用其中一种方法?
getField
可以获得从超类继承的字段,但 getDeclaredField
不能。 getDeclaredField
将自身限制为调用函数的类。
getField
无法访问私有成员
整个类层次结构中的所有 public
字段。
所有字段,无论其可访问性如何,但仅适用于当前类,而不是当前类可能继承的任何基类。
为了使层次结构中的所有字段上升,我编写了以下函数:
public static Iterable<Field> getFieldsUpTo(@Nonnull Class<?> startClass,
@Nullable Class<?> exclusiveParent) {
List<Field> currentClassFields = Lists.newArrayList(startClass.getDeclaredFields());
Class<?> parentClass = startClass.getSuperclass();
if (parentClass != null &&
(exclusiveParent == null || !(parentClass.equals(exclusiveParent)))) {
List<Field> parentClassFields =
(List<Field>) getFieldsUpTo(parentClass, exclusiveParent);
currentClassFields.addAll(parentClassFields);
}
return currentClassFields;
}
提供 exclusiveParent
类是为了防止从 Object
检索字段。如果您确实需要 Object
字段,它可能是 null
。
澄清一下,Lists.newArrayList
来自 Guava。
更新
仅供参考,上述代码发布在 GitHub 上我的 LibEx 项目 ReflectionUtils 中。
如前所述,Class.getDeclaredField(String)
仅查看您调用它的 Class
中的字段。
如果要在 Class
层次结构中搜索 Field
,可以使用以下简单函数:
/**
* Returns the first {@link Field} in the hierarchy for the specified name
*/
public static Field getField(Class<?> clazz, String name) {
Field field = null;
while (clazz != null && field == null) {
try {
field = clazz.getDeclaredField(name);
} catch (Exception e) {
}
clazz = clazz.getSuperclass();
}
return field;
}
例如,这对于从超类中查找 private
字段很有用。另外,如果你想修改它的值,你可以像这样使用它:
/**
* Sets {@code value} to the first {@link Field} in the {@code object} hierarchy, for the specified name
*/
public static void setField(Object object, String fieldName, Object value) throws Exception {
Field field = getField(object.getClass(), fieldName);
field.setAccessible(true);
field.set(object, value);
}
try try { field = clazz.getDeclaredField(name); } catch (NoSuchFieldException e) { clazz = clazz.getSuperclass(); if(clazz==null){ throw e; } }
public Field[] getFields() throws SecurityException
返回一个包含 Field 对象的数组,该数组反映了此 Class 对象表示的类或接口的所有可访问公共字段。返回的数组中的元素没有排序,也没有任何特定的顺序。如果类或接口没有可访问的公共字段,或者它表示数组类、原始类型或 void,则此方法返回长度为 0 的数组。
具体来说,如果此 Class 对象表示一个类,则此方法返回该类及其所有超类的公共字段。如果此 Class 对象表示一个接口,则此方法返回此接口及其所有超接口的字段。
此方法不反映数组类的隐式长度字段。用户代码应该使用类 Array 的方法来操作数组。
public Field[] getDeclaredFields() throws SecurityException
返回一个 Field 对象数组,反映由此 Class 对象表示的类或接口声明的所有字段。这包括公共、受保护、默认(包)访问和私有字段,但不包括继承的字段。返回的数组中的元素没有排序,也没有任何特定的顺序。如果类或接口未声明任何字段,或者此 Class 对象表示原始类型、数组类或 void,则此方法返回长度为 0 的数组。
如果我需要所有父类的所有字段怎么办?需要一些代码,例如来自 https://stackoverflow.com/a/35103361/755804:
public static List<Field> getAllModelFields(Class aClass) {
List<Field> fields = new ArrayList<>();
do {
Collections.addAll(fields, aClass.getDeclaredFields());
aClass = aClass.getSuperclass();
} while (aClass != null);
return fields;
}
不定期副业成功案例分享
Field#get
和类似方法。换句话说,这种方法不允许当前类访问其超类的私有接口,与典型编译不允许的方式相同。setAccessible
更改范围并且没有安全管理器setAccessible
是否允许子类调用超类的私有成员?我以前从未考虑过它,因为我只在使当前类中的私有成员可访问的上下文中使用它。private
字段只能通过特定于类的getDeclaredFields
访问。每个字段(即使具有相同的类型和名称)都是不同的Field
实例。