Java反射中的getFields和getDeclaredFields有什么区别

在使用Java反射时,我对getFields方法和getDeclaredFields方法之间的区别有点困惑。

我读到getDeclaredFields让你访问类的所有字段,而getFields只返回公共字段。如果是这样的话,为什么不总是使用getDeclaredFields呢?

有人能详细说明这一点,并解释这两种方法之间的区别,以及什么时候/为什么你想要使用其中一种而不是另一种?

119973 次浏览

getFields() . getFields(

所有public字段在整个类层次结构上。

getDeclaredFields() .

所有的字段,不管它们的可访问性,但仅针对当前类,而不是当前类可能从其继承的任何基类。

为了让所有的字段在层次结构中上升,我写了下面的函数:

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);
}

public Field[] getFields() throws SecurityException

返回一个包含Field对象的数组,该数组反映了由这个class对象表示的类或接口的所有可访问公共字段。返回的数组中的元素没有排序,也没有任何特定的顺序。如果类或接口没有可访问的公共字段,或者它表示数组类、基元类型或void,则此方法返回长度为0的数组。

具体来说,如果这个Class对象表示一个类,该方法返回这个类及其所有超类的公共字段。如果这个Class对象表示一个接口,该方法返回该接口及其所有超接口的字段。

此方法不反映数组类的隐式长度字段。用户代码应该使用Array类的方法来操作数组。


public Field[] getDeclaredFields() throws SecurityException

返回Field对象的数组,该数组反映了由这个Class对象表示的所有类声明的字段或接口。这个包括公共、受保护、默认(包)访问和私有字段,但是< em >不包括继承了< / em >字段。返回的数组中的元素没有排序,也没有任何特定的顺序。如果类或接口没有声明字段,或者此class对象表示基本类型、数组类或void,则此方法返回长度为0的数组。


如果我需要所有父类的所有字段怎么办? 需要一些代码,例如:from 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;
}