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