【问题标题】:Access to private inherited fields via reflection in Java通过 Java 中的反射访问私有继承字段
【发布时间】:2011-04-03 19:13:25
【问题描述】:

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

【问题讨论】:

  • “私有继承字段”不存在。如果一个字段是私有的,则它不会被继承,并且只保留在父类的范围内。要访问父私有字段,您必须先访问父类(参见 aioobe 的响应)
  • 也就是说,受保护的字段是继承的,但您必须这样做才能通过反射获取它们。

标签: java reflection inheritance private-members


【解决方案1】:

这应该演示如何解决它:

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。
  • 为什么不使用getDeclaredFields()[0] 或getDeclaredField("i") 而是在接下来的两个语句中重复[0] 数组访问?
  • 这是由于这个特定问题的表述方式。它基本上只是演示如何使用getDeclaredFields。答案已更新。
【解决方案2】:

这里最好的方法是使用Visitor Pattern 查找类和所有超类中的所有字段并对其执行回调操作。


实施

Spring 有一个很好的实用程序类 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 中找到字段 private transient boolean javax.management.relation.RoleUnresolvedList.typeSafe
在类型类 javax.management.relation.RoleUnresolvedList
中找到字段 private transient boolean javax.management.relation.RoleUnresolvedList.tainted 在类型类 java.util.ArrayList
中找到字段 private transient java.lang.Object[] java.util.ArrayList.elementData 在类型类 java.util.ArrayList
中找到字段 private int java.util.ArrayList.size 在类型类 java.util.AbstractList 中找到字段 protected transient int java.util.AbstractList.modCount

【讨论】:

  • 这不是“访问者模式”,但如果您的代码中有 Spring 病毒,它仍然是一个非常好的工具。感谢分享:)
  • @jose.diego 我很确定你可以为此争论。它访问的是类层次结构而不是对象树,但原理保持不变
  • 不确定此评论是否会得到回复,但您使用此解决方案一次只能访问特定字段。如果我需要同时查看其他字段——例如,如果另一个字段为 NULL,则将此字段设置为“abc”——我没有可用的整个对象。
  • 这个类的 JavaDoc 表明“它仅供内部使用”太糟糕了,所以这对任何希望使用它的人来说都是一个可能的风险。
  • @spacemanspiff 你在技术上是正确的,但是这个类已经存在了大约 15 年(包括 4 个主要发布版本)并且被许多 Spring 客户广泛使用。我怀疑他们现在会把它拉回来。
【解决方案3】:

这样就可以了:

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 的情况下,这些字段被标记为 synthetic,您可以像这样过滤掉它们:

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 也是如此。
  • 这得到了参数类的声明和继承字段,因此应该命名为 getDeclaredAndInheritedPrivateFields。完美不过谢谢!
  • isSynthetic 不错 :)
  • 感谢您的精彩回答~
【解决方案4】:
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);
}

(基于this的回答)

【讨论】:

    【解决方案5】:

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

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

    【讨论】:

    • 但是,他的解决方案确实让您走上了正确的道路,不是吗?
    • Vector 是糟糕的旧代码。请使用集合框架中的当前数据结构(大多数情况下 ArrayList 就足够了)
    • @aperkins aioobe 的答案看起来像我的,但我找到了它,然后我看到了答案。 @seanizer Vector 并不老,它是集合 API 的成员
    • “从 Java 2 平台 v1.2 开始,这个类已经被改造为实现 List,因此它成为 Java 集合框架的一部分。”在 1.2 中改装?如果那不是旧的,那是什么?来源:download.oracle.com/javase/1.4.2/docs/api/java/util/Vector.html
    • Vector 的开销很大,因为一切都是同步的。在需要同步的地方,java.util.concurrent 中有更好的类。 Vector、Hashtable和StringBuffer在大多数情况下应该被ArrayList、HashMap和StringBuilder代替
    【解决方案6】:

    我需要为 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);
      }
      

      【讨论】:

        猜你喜欢
        • 2015-07-08
        • 2010-10-15
        • 2010-11-17
        • 2010-12-06
        • 2020-08-04
        • 2013-01-09
        • 2014-06-01
        • 2010-12-18
        • 2021-12-02
        相关资源
        最近更新 更多