Wednesday, 19 September 2012

Puzzle 79: It's a Dog's Life


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!