Java Reference
In-Depth Information
On casual inspection, it would appear that this program should just print
woof
. After all,
Basenji
extends
Dog
and defines its
bark
method to do nothing. The
main
method invokes the
bark
method,
first on
woofer
the
Dog
and again on
nipper
the
Basenji
. Basenjis don't bark, but apparently this
one does. If you ran the program, you found that it prints
woof woof
. What is the matter with poor
Nipper?
The title of this puzzle gives a big hint. The problem is that
bark
is a static method, and
there is no
dynamic dispatch on static methods
[JLS 15.12.4.4]. When a program calls a static method, the
method to be invoked is selected at compile time, based on the compile-time type of the
qualifier
,
which is the name we give to the part of the method invocation expression to the left of the dot. In
this case, the qualifiers of the two method invocations are the variables
woofer
and
nipper
, both of
which are declared to be of type
Dog
. Because they have the same compile-time type, the compiler
causes the same method to be invoked:
Dog.bark
. This explains why the program prints
woof woof
.
It doesn't matter that the runtime type of
nipper
is
Basenji
; only its compile-time type is
considered.
To fix this program, simply remove the
static
modifier from the two
bark
method declarations.
Then the
bark
method in
Basenji
will
override
rather than
hide
the
bark
method in
Dog
, and the
program will print
woof
instead of
woof woof
. With overriding, you get dynamic dispatch; with
hiding, you don't.
When you invoke a static method, you typically qualify it with a class rather than an expression: for
example
Dog.bark
or
Basenji.bark
. When you read a Java program, you expect classes to be used
as the qualifiers for static methods, which are statically dispatched, and expressions to be used as
the qualifiers for instance methods, which are dynamically dispatched. Coupled with the different
naming conventions for classes and variables, this provides a strong visual cue as to whether a given
method invocation is static or dynamic. The program in this puzzle uses an expression as the
qualifier for a static method invocation, which is misleading.
Never qualify a static method
invocation with an expression.
The confusion is compounded by the appearance of overriding. The
bark
method in
Basenji
has
the same signature as the
one
in
Dog
. That is the usual formula for overriding, which suggests
dynamic dispatch. In this case, however, the methods are declared
static
. Static methods cannot be
overridden; they can only be hidden, and just because you can doesn't mean you should. To avoid
confusion,
do not hide static methods
.
There is nothing to gain, and much to lose, from reusing the
name of a superclass's static method in a subclass.
The lesson for language designers is that invocations of class and instance methods should look
different from each other. One way to further this goal is to disallow the use of expressions as
qualifiers for static methods. A second way to distinguish static and instance method invocations is
to use different operators, as C++ does. A third alternative is to finesse the issue by dispensing with
the concept of static methods altogether, as Smalltalk does.
In summary, qualify static methods invocations with a class name, or don't qualify them at all if
you're invoking them from within their own class, but never qualify them with an expression. Also,
avoid hiding static methods. Together, these guidelines help eliminate the misleading appearance of
overriding with dynamic dispatch for static methods.
< Day Day Up >
Search WWH ::
Custom Search