05 February, 2016

Finer Points in Java: Shadowing vs Overriding

I have recently run into a "feature" of Java which really caused me a lot of head scratching and testing before I discovered the core of a recent coding problem.  Because I was unclear of certain peculiarities of shadowing and overriding, I was expecting A, when in fact I got B.

The Problem


In a nutshell, shadowed variables come from the declared type of an object, but overridden methods are run from the constructed object.

Ok, that isn't very clear, so here is some code to make it simpler.  It's all in one file called Derived.java:


class Base {
    public int x = 0;
    public String aMethod() {
        return "Base";
    };
}

public class Derived extends Base {
    private String secretmessage = "a secret!";
    public int x = 1;
    public String aMethod() {
        return "Derived " + this.secretmessage;
    }
    
    public static void main(String[] args) {
        Base b = new Derived();
        //
        System.out.println("x:aMethod = " + b.x + ":" + b.aMethod());
        //
    }
}


The output of this when run is:

x:aMethod = 0:Derived a secret!

What that shows is that b.x returned the Base class's variable x, and b.aMethod() returned the Derived class's method aMethod.

Access to Unreachable Data


In Derived, the function aMethod returns a string that contains the contents of the private instance variable secretmessage.  If I were to substitute b.aMethod() with b.secretmessage, I would get a compiler error because b.secretmessage is not available in class B:

Derived.java:23: error: cannot find symbol
        System.out.println("x:aMethod = " + b.x + ":" + b.secretmessage);
                                                         ^
  symbol:   variable secretmessage
  location: variable b of type Base
1 error

But, I can access the variable via aMethod!  This also means I could execute any method in Derived, as long as it overrides the same method in Base.

If an able Java programmer did not know this, then reading the code may be problematic: one would expect a base class's method to run when in fact the derived class method was run.  Likewise, the opposite could be said for a shadowed variable.

Bottom line: be aware of the shadowing and overriding rules.