Test Your Understanding of Scope under Inheritance

Written by Wayne Pollock, Tampa Florida USA, 2002

(Last updated 10/2013)

 

When extending one class with another there are several possibilities of redefining variables and methods.  When the same name is used in both the parent class and the child class, referring to different fields or methods, things can become confusing.  The different possibilities are sometimes referred to as shadowing, overriding, hiding, and obscuring.  How well do you understand these concepts?  (A few additional notes and references are listed at the end.)

Try to predict the output of each of the Java programs below:

Method Inheritance

// What is the output of the following code?

public class TestInheritance {
   public static void main ( String [] args ) {
      ChildClass c = new ChildClass();
      c.showID();
  }
}

class ParentClass {
   int id = 1;
   void showID() {
     System.out.println( id );
   }
}

class ChildClass extends ParentClass {
   int id = 2;
}
 
See the answer for method inheritance Back to top

 


Shadowing

    // Shadowing:  What is the output?
    public class Shadow
    {   int a;
        int b;

        public Shadow ( int a, int b )
        {   a = a;
            this.b = b;
        }

        public static void main ( String [] args )
        {   Shadow s = new Shadow( 37, 47 );
            System.out.println( "a = " + s.a );
            System.out.println( "b = " + s.b );
        }
    }

 
See the answer for shadowing Back to top

 


Overriding

    // Overriding:  What is the output?
    class A
    {   void f ()
        {   System.out.println( "A.f" );
        }
    }
    
    public class Override extends A
    {   void f ()
        {   System.out.println( "Override.f" );
        }

        void g ()
        {   f();
            super.f();
        }
    
        public static void main ( String [] args )
        {   Override o = new Override();
            o.g();
        }
    }
 
See the answer for overriding Back to top

 


Hiding

    // Hiding: What's the output?
    class Base
    {   static void f ()
        {   System.out.println( "Base.f" );
        }

        void g ()
        {   System.out.println( "Base.g" );
        }
    }
    
    public class Hide extends Base
    {   static void f ()
        {   System.out.println( "Hide.f" );
        }

        void g ()
        {   System.out.println( "Hide.g" );
        }

        public static void main ( String [] args )
        {   Base ref = new Hide();
            ref.f();
            ref.g();
        }
    }
 
See the answer for hiding Back to top

 


Obscuring

    // Obscuring: What's the output?
    class A
    {   static int MIN_PRIORITY = 59;
    }
    
    public class Obscure
    {   static A Thread;
    
        public static void main ( String [] args )
        {   System.out.println( Thread.MIN_PRIORITY );
            System.out.println( java.lang.Thread.MIN_PRIORITY );
        }
    }
 
See the answer for obscuring Back to top





























Answer (Method Inheritance)

When your run TestInheritance, you should see:

    1

Many are fooled into thinking the output should be “2” instead.  The key is to realize that ChildClass objects contain two fields named “id”.  The inherited method only knows about the fields from ParentClass.  To refer to the “id” field defined in ChildClass, you need to override the method (or define a new method).

 

 
Back to Method Inheritance Question Next Question (Shadowing) Back to top

















Answer (Shadowing)

When your run Shadow, you should see:

    a = 0
    b = 47

One place shadowing comes up is when you have field names and parameter names in a constructor that are the same, and you want to use the parameters to set the fields:

    class BadShadow
    {   int a;

        BadShadow ( int a )
        {   a = a;  // Error!
        }
    }

This doesn't work because the parameter “ashadows the field “a”.  That is, the parameter name blocks access to the field name.  You can get around this problem by saying:

    class GoodShadow
    {   int a;

        GoodShadow ( int a )
        {   this.a = a;  // works as expected
        }
    }

The point in using the same names is that you don't have to invent parameter names like “a1” or “_a”.

 

 
Back to Shadowing Question Next Question (Overriding) Back to top

















Answer (Overriding)

When you run Override, you should see:

    Override.f
    A.f

In this example, the method Override.f overrides the method A.f.  If you have an object of type Override and call f, Override.f is called.  However if you have an object of type A, A.f is called.  This approach is a standard part of object-oriented programming.  For example java.lang.Object declares a hashCode method, but subclasses such as String provide an overriding version of the method.  The overriding version is tailored to the particular type of data represented by the class.

You can call the superclass method by using the notation:

    super.f();
 
Back to Overriding Question Next Question (Hiding) Back to top

















Answer (Hiding)

When your run Hide, you should see:

    A.f
    Hide.g

In this example, Hide.f hides A.f, and Hide.g overrides A.g.  One way of seeing the difference between hiding and overriding is to note that overriding applies to regular instance methods.  The actual method that is called is determined at run time based on the type of the object (a so-called virtual function).  This sort of dynamic lookup does not happen for static methods or for fields.

For example, in this code:

    class Parent
    {   int x = 37;

        void f ()
        {   System.out.println( "Parent.f" );
        }
    }
    
    public class Child extends Parent
    {
        int x = 47;

        void f ()
        {   System.out.println( "Child.f" );
        }

        public static void main (String [] args )
        {   Parent ref = new Child();
            ref.f();  // call Child.f
            System.out.println( ref.x );  // display Parent.x
        }
    }

The method reference through “ref” results in Child.f being called, but the field reference obtains Parent.x.  Or to say it another way, the actual class of an object determines which instance methods are called, but for fields and static methods the type of the reference determines which field is accessed or which static method is invoked.

So when you run Child you should see:

    Child.f
    37

 

 
Back to Hiding Question Next Question (Obscuring) Back to top

















Answer (Obscuring)

When your run Obscure, you should see:

    59
    1

Consider the first print statement in this example, that prints:

    Thread.MIN_PRIORITY

The use of Thread is ambiguous.  The two possible meanings for this expression are: either the static field MIN_PRIORITY in the class java.lang.Thread, (If you think Thread refers to a class) or the static field MIN_PRIORITY in the class variable Thread of class A (if you think Thread refers to a variable).

The Java language specification says that in this situation variables are chosen in preference to types (classes).  So the static field in the class variable Thread is printed.

You can work around this by fully qualifying the class name Thread, as the example shows:

    java.lang.Thread.MIN_PRIORITY

This example represents a poor coding style.  See the additional notes below for more details.

 

 
Back to Obscuring Question Back to top

















Additional Notes

Just because the Java language allows you to do something, it doesn't mean that it's a good thing to do.  For example, it's legal to say:

    class A
    {
        int A;
    }

in a program, but you probably shouldn't because it's confusing.

The best way to handle issues with conflicting names is to simply avoid them as far as possible.  For example, you can avoid many problems if you follow the standard Java coding conventions that specifies that the first letter of a type name (such as “class A”) should be capitalized, while the first letter of a field name (such as “int A”) should be lowercase.

 

References

For more information about shadowing, see section 6.3.2 Obscured Declarations, section 7.5.2 Type-Import-on-Demand Declaration, section 8.4.6 Inheritance, Overriding, and Hiding, section 8.4.8.5 Example: Invocation of Hidden Class Methods, and section 14.4.3 Shadowing of Names by Local variables, in The Java Language Specification Second Edition by Gosling, Joy, Steele, and Bracha ( java.sun.com/docs/books/jls/).

Most of this article was adopted from an article appearing the Sun's Java Developer Connection, October 10, 2000 issue of Tech Tips.