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:
// 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: 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: 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: 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: 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 |
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 |
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 “a
”
shadows 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 |
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 |
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 |
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 |
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.
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.