通过Java中的反射访问私有继承的字段


109

我找到了一种通过来获取继承成员并通过来获得 class.getDeclaredFields(); 私有成员的方法,class.getFields() 但是我正在寻找私有的继承字段。我怎样才能做到这一点?


28
“私有继承字段”不存在。如果字段是私有的,则该字段不会被继承,并且仅保留在父类的范围内。要访问父级私有字段,您必须先访问父级类(请参阅aioobe的回复)
Benoit Courtine 2010年

6
也就是说,受保护的字段是继承的,但是您必须做同样的事情才能通过反射获得它们。
2010年

Answers:


128

这应该演示如何解决它:

import java.lang.reflect.Field;

class Super {
    private int i = 5;
}

public class B extends Super {
    public static void main(String[] args) throws Exception {
        B b = new B();
        Field f = b.getClass().getSuperclass().getDeclaredField("i");
        f.setAccessible(true);
        System.out.println(f.get(b));
    }
}

(或Class.getDeclaredFields用于所有字段的数组。)

输出:

5

这会获得所有超类的字段还是仅获得直接超类?
下雨

直接超类的字段。如果您想走得更高,就可以递归getSuperclass()直到到达null
aioobe '19

为什么不使用getDeclaredFields()[0]getDeclaredField("i")而是[0]在接下来的两个语句中重复数组访问?
霍尔格

这是由于此特定问题的表达方式。基本上只是一个如何使用的演示getDeclaredFields。答案已更新。
aioobe

44

最好的方法是使用“ 访问者模式”在类和所有超类中查找所有字段,并对它们执行回调操作。


实作

Spring有一个很好的Utility类ReflectionUtils,它可以做到这一点:它定义了一种方法,用于通过回调在所有超类的所有字段上循环:ReflectionUtils.doWithFields()

说明文件:

在目标类的所有字段上调用给定的回调,在类层次结构中向上获取所有已声明的字段。

参数:
-clazz-要分析的目标类-fc-
每个字段要调用的回调
-ff-确定将回调应用到的字段的过滤器

样例代码:

ReflectionUtils.doWithFields(RoleUnresolvedList.class,
    new FieldCallback(){

        @Override
        public void doWith(final Field field) throws IllegalArgumentException,
            IllegalAccessException{

            System.out.println("Found field " + field + " in type "
                + field.getDeclaringClass());

        }
    },
    new FieldFilter(){

        @Override
        public boolean matches(final Field field){
            final int modifiers = field.getModifiers();
            // no static fields please
            return !Modifier.isStatic(modifiers);
        }
    });

输出:

在类型类javax.management.relation.RoleUnresolvedList发现场私人短暂布尔javax.management.relation.RoleUnresolvedList.typeSafe
型类javax.management.relation.RoleUnresolvedList发现场私人短暂布尔javax.management.relation.RoleUnresolvedList.tainted
发现场类型类java.util.ArrayList中的私有瞬态java.lang.Object [] java.util.ArrayList.elementData
找到类型类java.util.ArrayList中的私有int java.util.ArrayList.size类型
发现了受保护的瞬态int java域。类型类java.util.AbstractList中的util.AbstractList.modCount


3
那不是“访客模式”,但是如果您的代码中包含Spring病毒,它仍然是一个非常好的工具。感谢您分享:)
thinlizzy 2013年

2
@ jose.diego我很确定你可以为此辩护。它访问的是类层次结构,而不是对象树,但是原理保持不变
肖恩·帕特里克·弗洛伊德

不确定此评论是否会得到答复,但是使用此解决方案,您一次只访问特定的字段。如果我需要同时查看其他字段-例如,如果另一个字段为NULL,则将该字段设置为“ abc”-我没有整个对象可用。
基因b。

太糟糕了,该类的JavaDoc指示“仅供内部使用”,因此这对希望使用它的任何人都是可能的风险。
太空人spiff

1
@spacemanspiff从技术上讲您是正确的,但是该课程已经存在了15年左右(包括4个主要发行版本),并且已被许多Spring客户广泛使用。我怀疑他们会把它拉回来。
肖恩·帕特里克·弗洛伊德

34

这样就可以了:

private List<Field> getInheritedPrivateFields(Class<?> type) {
    List<Field> result = new ArrayList<Field>();

    Class<?> i = type;
    while (i != null && i != Object.class) {
        Collections.addAll(result, i.getDeclaredFields());
        i = i.getSuperclass();
    }

    return result;
}

如果使用EclEmma之类的代码覆盖工具,则必须当心:它们为每个类都添加了一个隐藏字段。在EclEmma的情况下,这些领域是明显的合成,而且可以过滤它们像这样:

private List<Field> getInheritedPrivateFields(Class<?> type) {
    List<Field> result = new ArrayList<Field>();

    Class<?> i = type;
    while (i != null && i != Object.class) {
        for (Field field : i.getDeclaredFields()) {
            if (!field.isSynthetic()) {
                result.add(field);
            }
        }
        i = i.getSuperclass();
    }

    return result;
}

感谢您对合成领域的评论,EMMA也是一样。
Anatoliy 2012年

这将获取参数类的声明字段和继承字段,因此应将其命名为getDeclaredAndInheritedPrivateFields。完美,虽然谢谢!
彼得·霍金斯

1
isSynthetic上的好消息:)
Lucas Crawford

感谢您的出色回答
〜–下雨了

19
public static Field getField(Class<?> clazz, String fieldName) {
    Class<?> tmpClass = clazz;
    do {
        try {
            Field f = tmpClass.getDeclaredField(fieldName);
            return f;
        } catch (NoSuchFieldException e) {
            tmpClass = tmpClass.getSuperclass();
        }
    } while (tmpClass != null);

    throw new RuntimeException("Field '" + fieldName
            + "' not found on class " + clazz);
}

(基于答案)


15

实际上,我使用的是复杂类型的层次结构,因此您的解决方案并不完整。我需要进行递归调用以获取所有私有继承字段。这是我的解决方案

 /**
 * Return the set of fields declared at all level of class hierachy
 */
public Vector<Field> getAllFields(Class clazz) {
    return getAllFieldsRec(clazz, new Vector<Field>());
}

private Vector<Field> getAllFieldsRec(Class clazz, Vector<Field> vector) {
    Class superClazz = clazz.getSuperclass();
    if(superClazz != null){
        getAllFieldsRec(superClazz, vector);
    }
    vector.addAll(toVector(clazz.getDeclaredFields()));
    return vector;
}

但是,他的解决方案确实使您走上了正确的道路,不是吗?
aperkins 2010年

1
向量是错误的旧代码。请使用集合框架中的当前数据结构(在大多数情况下ArrayList是足够的)
Sean Patrick Floyd 2010年

@aperkins aioobe的答案看起来像我的,但是我找到了,然后我看到了答案。@seanizer Vector是不是老了,it'a收集API中的一员
甲氧基苯

“从Java 2平台v1.2开始,该类已经过改进以实现List,因此它成为Java集合框架的一部分。” 在1.2中进行了改装?如果还不算老,那是什么?来源:download.oracle.com/javase/1.4.2/docs/api/java/util/Vector.html
肖恩·帕特里克·弗洛伊德

7
Vector的开销很大,因为所有内容都是同步的。在需要同步的地方,java.util.concurrent中还有更好的类。在大多数情况下,应将Vector,Hashtable和StringBuffer替换为ArrayList,HashMap和StringBuilder
Sean Patrick Floyd 2010年

8

我需要在Model Citizen中添加对蓝图的继承字段的支持。我派生了此方法,该方法对于检索类的字段+继承的字段更加简洁。

private List<Field> getAllFields(Class clazz) {
    List<Field> fields = new ArrayList<Field>();

    fields.addAll(Arrays.asList(clazz.getDeclaredFields()));

    Class superClazz = clazz.getSuperclass();
    if(superClazz != null){
        fields.addAll(getAllFields(superClazz));
    }

    return fields;
}

7
private static Field getField(Class<?> clazz, String fieldName) {
    Class<?> tmpClass = clazz;
    do {
        for ( Field field : tmpClass.getDeclaredFields() ) {
            String candidateName = field.getName();
            if ( ! candidateName.equals(fieldName) ) {
                continue;
            }
            field.setAccessible(true);
            return field;
        }
        tmpClass = tmpClass.getSuperclass();
    } while ( clazz != null );
    throw new RuntimeException("Field '" + fieldName +
        "' not found on class " + clazz);
}
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.