The following program models the behavioral difference between
Basenjis and other dogs. In case you didn't know, the Basenji is a breed of
small, curly-tailed dogs of African origin that do not bark. What does the
program print?
class Dog { public static void bark() { System.out.print("woof "); } } class Basenji extends Dog { public static void bark() { } } public class Bark { public static void main(String args[]) { Dog woofer = new Dog(); Dog nipper = new Basenji(); woofer.bark(); nipper.bark(); } }
Solution 48: All I Get Is Static
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
No comments:
Post a Comment
Your comments are welcome!