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


194

我对使用Java反射时的getFields方法和getDeclaredFields方法之间的区别感到困惑。

我读到它getDeclaredFields使您可以访问该类的所有字段,并且 getFields只返回公共字段。如果是这样,您为什么不总是使用getDeclaredFields

有人可以详细说明一下,并解释两种方法之间的区别,以及何时/为什么要在另一种方法上使用一种方法吗?


3
getField可以获取从超类继承的字段,但getDeclaredField不能。getDeclaredField将自身限制为调用该函数的类。
user2336315 2013年

@ user2336315是正确的,但是getField无法访问私有成员
Madbreaks'Aug

Answers:


258

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。可能是null您确实想要这些Object字段。

澄清一下,Lists.newArrayList来自番石榴。

更新资料

仅供参考,上面的代码在GitHub上,我发表LibEx项目ReflectionUtils


8
很好的答案,但应注意,当前类的实例不能将超类中的私有字段用于Field#get类似方法。换句话说,这种方法不会使当前的类访问其超类的专用接口,以同样的方式典型的编译没有。
FThompson

4
@Vulcan True,除非编写代码使用反射来更改范围,setAccessible并且没有适当的安全管理器
John B

轻微,应为“(无论是否可访问)”而不是“(无论范围)”。所有字段都具有相同的范围,即类的body
yshavit 2013年

@yshavit谢谢。更新。
约翰B

1
不会的。由于private只能通过getDeclaredFields特定于类的字段来访问字段。每个字段(甚至具有相同类型和名称的字段)都是不同的Field实例。
John B

7

如前所述,Class.getDeclaredField(String)仅查看Class您在其中调用的字段。

如果要FieldClass层次结构中搜索,可以使用以下简单功能:

/**
 * 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; } }
Sven Dhaens

5

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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.