如何使用Java反射访问超类的超类的私有字段?

7
在我使用的一个API中,有一个抽象类(Class A)拥有一个私有字段(A.privateField)。该API中还有一个继承自Class A的Class B。 我需要用我的实现Class C来扩展Class B,但我需要访问Class A的privateField。 我应该使用反射:如何访问超级父类的私有字段?
Class A
    - privateField
Class B extends A
Class C extends B
    + method use A.privateField

我认为Class A的私有字段可以改为受保护的,这样你就可以在Class C中直接访问它,而无需使用反射。 - conca
2个回答

17

你需要这样做的事实表明设计存在缺陷。

然而,可以按照以下方式完成:

class A
{
  private int privateField = 3;
}

class B extends A
{}

class C extends B
{
   void m() throws NoSuchFieldException, IllegalAccessException
   {
      Field f = getClass().getSuperclass().getSuperclass().getDeclaredField("privateField");
      f.setAccessible(true); // enables access to private variables
      System.out.println(f.get(this));
   }
}

用以下方式进行调用:

new C().m();

进行Andrzej Doyle所说的“向上遍历类层次结构”的方法之一是:

Class c = getClass();
Field f = null;
while (f == null && c != null) // stop when we got field or reached top of class hierarchy
{
   try
   {
     f = c.getDeclaredField("privateField");
   }
   catch (NoSuchFieldException e)
   {
     // only get super-class when we couldn't find field
     c = c.getSuperclass();
   }
}
if (f == null) // walked to the top of class hierarchy without finding field
{
   System.out.println("No such field found!");
}
else
{
   f.setAccessible(true);
   System.out.println(f.get(this));
}

另外,如果字段仅存在于“一个超类”上,而不确定是哪个超类,则可以沿着类层次结构向上查找,依次检查每个类以查看是否包含该字段(直到遇到“Object”为止)。 - Andrzej Doyle
谢谢!我开始使用反射,这是一个非常有用的起点! - Andrea T
是的,那就是我想表达的要点(注释并不适合用于代码)。但是 while (true) 意味着循环只能通过异常来结束(如果未找到字段,最终会抛出 NPE 异常)。while (c != null) 将是更好的条件。 - Andrzej Doyle
作为一个次要的问题,上面的示例在字段不存在的“通常”情况下会抛出异常,这是非常昂贵的。如果这将用于库或性能关键代码中,则最好调用c.getDeclaredFields,然后在返回的Field对象上调用getName(),直到找到具有给定名称的字段(或数组耗尽)。编写起来更复杂,但运行速度更快,因此值得记住。 - Andrzej Doyle
@AndrzejDoyle 更改了检查条件为 c != null 并改进了错误检查。 - Bernhard Barker
1
@AndrzejDoyle 您关于使用 getDeclaredFields 是正确的。getDeclaredField 无论如何都会搜索所有字段,因此我不认为它有任何优势,除了简单(这通常已经足够了)。我必须说我对这两种方法都不是特别喜欢。如果有一个使用 Map 并返回 null 而不是抛出异常的 getDeclaredField 或类似的 hasDeclaredField 就好了。 - Bernhard Barker

0

另一种方法,如果你正在使用springframework。

// Attempt to find a field on the supplied Class with the supplied name. Searches all superclasses up to Object.
Field field = ReflectionUtils.findField(C.class, "privateField");

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接