2

如何访问protected基类的基类中被基类中的字段隐藏的字段?

一个例子:

package foo;

public class Foo {
    protected int x;

    int getFooX() { return x; }
}
package bar;

public class Bar extends foo.Foo {
    protected int x;

    // Can access foo.Foo#x through super.x
}

Foo类的x字段被 的同名字段遮蔽,Bar但可以通过反射访问:

package baz;

public class Baz extends bar.Bar {
    {
        // Want getFooX() to return 2
        // ((foo.Foo) this).x = 2;  // Fails due to access error; Makes sense
        // super.x = 2;  // Changes bar.Bar#x
        // super.super.x = 2;  // Syntax error
        // foo.Foo.this.x = 2;  // Syntax error
        try {
            Field Foo_x = foo.Foo.class.getDeclaredField("x");
            Foo_x.setAccessible(true);
            Foo_x.setInt(this, 2);
        } catch (ReflectiveOperationException e) { e.printStackTrace(); }
        // Doesn't compile error if field changes name
    }
}

有没有一种方法可以做到这一点而无需反思,也无需更改超类?

4

2 回答 2

1

不工作?

public static void main(String... args) {

        Baz b = new Baz();

        try {
            Field Foo_x = Foo.class.getDeclaredField("x");
            Foo_x.setAccessible(true);
            Foo_x.setInt(b, 2);
            System.out.println(b. getFooX());
        } catch (ReflectiveOperationException e) {
            e.printStackTrace();
        }
    }
于 2019-11-23T23:24:18.073 回答
1

如果这些类在同一个包中,那么您可以强制this转换为要访问的字段的类型。字段名称是静态解析的,基于左侧表达式的编译时类型.,因此下面的代码访问A的字段,因为表达式((A) this)具有编译时类型A

class A {
    protected int x = 1;
}

class B extends A {
    protected int x = 2;
}

class C extends B {
    int getAx() {
        return ((A) this).x;
    }

    void setAx(int x) {
        ((A) this).x = x;
    }
}

请注意,这只适用于类在同一个包中的情况。在您的示例中,这些类位于不同的包中,因此因为该字段是protected您将收到编译错误。

在这种情况下,由于 Java 语言规范的这一部分(第6.6.2 节,强调我的),没有反射就不可能访问A' 字段:

令 C 为声明受保护成员的类。只有在 C 的子类 S 的主体内才允许访问。

此外,如果 Id 表示实例字段或实例方法,则:

  • ...
  • 如果通过字段访问表达式 E.Id、方法调用表达式 E.Id(...) 或方法引用表达式 E :: Id 进行访问,其中 E 是主表达式(第 15.8 节),则当且仅当 E 的类型是 S 或 S 的子类时才允许访问

在这里,您正在编写的类是C,因此超类的受保护字段A只能由表达式访问,例如(expr).x表达式的类型是C或 的子类C。但在这种情况下,.x将始终解析为B' 字段而不是A' 字段。

从逻辑上讲,A任何字段访问表达式都不允许访问 的字段C

我们还可以排除可以访问字段的其他类型的表达式:简单的名称 x不起作用,超级表达式不能像链接一样super.super.x(参见这个答案)。

于 2019-11-23T23:35:36.083 回答