从隐藏它的类的子类访问隐藏字段

时间:2019-11-23 22:27:49

标签: java member-hiding

如何访问基类中被字段隐藏的基类中的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
    }
}

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

2 个答案:

答案 0 :(得分: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();
        }
    }

答案 1 :(得分: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,因此,如果表达式的类型为{{1,则只能通过A之类的表达式访问超类(expr).x的受保护字段}}或C的子类。但是在那种情况下,C将始终解析为.x的字段,而不是B的字段。

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

我们还可以排除可以访问字段的其他类型的表达式:simple name C不起作用,super expression不能像{{1} }(请参阅this answer)。