Search code examples
javamember-hiding

Access hidden field from subclass of class that hides it


How can you access a protected field in a base class's base class that is hidden by a field in a base class?

An example:

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
}

The Foo class's x field is shadowed by Bar's field of the same name, but can be accessed by reflection:

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
    }
}

Is there a way to do this without reflection, and without making changes to the superclasses?


Solution

  • If the classes are in the same package, then you can cast this to the type you want to access the field of. Field names are resolved statically, based on the compile-time type of the expression on the left of the ., so the code below accesses A's field because the expression ((A) this) has compile-time type 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;
        }
    }
    

    Note that this works only in the case where the classes are in the same package. In your example the classes are in different packages, so because the field is protected you'll get a compilation error.

    In this case, it is impossible to access A's field without reflection, because of this part of the Java Language Specification (§6.6.2, emphasis mine):

    Let C be the class in which a protected member is declared. Access is permitted only within the body of a subclass S of C.

    In addition, if Id denotes an instance field or instance method, then:

    • ...
    • If the access is by a field access expression E.Id, or a method invocation expression E.Id(...), or a method reference expression E :: Id, where E is a Primary expression (§15.8), then the access is permitted if and only if the type of E is S or a subclass of S.

    Here the class you're writing in is C, so the protected field of the superclass A is only accessible by an expression like (expr).x if the expression's type is C or a subclass of C. But in that case, the .x will always resolve to B's field rather than A's field.

    Logically, it follows that accessing A's field is not permitted by any field access expression in C.

    We can also rule out the other kinds of expressions which can access a field: a simple name x doesn't work, and a super expression cannot be chained like super.super.x (see this answer).