This class models the life of a house
pet. The main method creates a Pet instance representing a dog
named Fido and lets it run. Although most dogs run in the backyard, this one
runs in the background. What does the program print?
public class Pet {
public final String name;
public final String food;
public final String sound;
public Pet(String name, String food, String sound) {
this.name = name;
this.food = food;
this.sound = sound;
}
public void eat() {
System.out.println(name + ": Mmmmm, " + food);
}
public void play() {
System.out.println(name + ": " + sound + " " + sound);
}
public void sleep() {
System.out.println(name + ": Zzzzzzz...");
}
public void live() {
new Thread() {
public void run() {
while (true) {
eat();
play();
sleep();
}
}
}.start();
}
public static void main(String[] args) {
new Pet("Fido", "beef", "Woof").live();
}
}
Solution 79: It's a Dog's Life
The main method creates a
Pet instance representing Fido and invokes its live method.
The live method, in turn, creates and starts a Thread that
repeatedly executes the eat, play, and sleep methods
from the enclosing Pet instance. Forever. Each of those methods prints
a single line, so one would expect the program to print these three lines
repeatedly:
Fido: Mmmmm, beef Fido: Woof Woof Fido: Zzzzzzz...
If you tried the program, you found that it won't even compile.
The compiler error is less than helpful:
Pet.java:28: cannot find symbol
symbol: method sleep()
sleep();
^
Why can't the compiler find the symbol? It's right there in
black and white. As in Puzzle 74, the problem stems from the
details of the overload resolution process. The compiler searches for the method
in the innermost enclosing scope containing a method with the correct name [JLS
15.12.1]. For the sleep invocation in our program, that scope is the
anonymous class containing the invocation, which inherits the methods
Thread.sleep(long) and THRead.sleep(long,int). These are the
only methods named sleep in that scope, and neither is applicable to
this invocation because both require parameters. As neither candidate for the
invocation is applicable, the compiler prints an error message.
The sleep methods inherited into the anonymous class
from Thread shadow [JLS 6.3.1] the
desired sleep method. As you saw in Puzzles 71 and 73, you should avoid shadowing. The shadowing in this puzzle is
indirect and unintentional, which makes it even more insidious than usual.
The obvious way to fix the program is to change the name of the
sleep method in Pet to snooze, doze, or
nap. Another way to fix the problem is to name the class explicitly in
the method invocation, using the qualified
this construct [JLS 15.8.4]. The resulting invocation is
Pet.this.sleep().
The third and arguably best way to fix the problem is to take
the advice of Puzzle
77 and use the THRead(Runnable)
constructor instead of extending Thread.
If you do this, the problem goes away because the anonymous class does not
inherit THRead.sleep. This simple modification to the program produces
the expected, if tiresome, output:
public void live() {
new Thread(new Runnable() {
public void run() {
while (true) {
eat();
play();
sleep();
}
}
}).start();
}
In summary, beware of unintentional shadowing, and learn to
recognize compiler errors that indicate its presence. For compiler writers, do
your best to generate error messages that are meaningful to the programmer. In
this case, for example, the compiler could alert the programmer to the existence
of a shadowed method declaration that is applicable to the invocation.
No comments:
Post a Comment
Your comments are welcome!