Thursday, February 5, 2009

Mental Models

In Java, an anonymous inner class is permitted to reference the variables and parameters defined by its enclosing method — but only if they're declared as final. For example, the Runnable below can use foo1, but not foo2:

public Runnable prepareOperation(final Foo obj1)
{
    Foo foo2 = // get another instance
    return new Runnable()
    {
        public void run()
        {
            // do something
        }
    };
}

Why the restriction? The JLS simply says that it exists. The Inner Classes Specification (which only appears to exist as part of the JDK 1.1.8 documentation set) requires that “the same value is produced everywhere, so that the name consistently appears to refer to the same variable in all parts of its scope,“ but doesn't give a rationale.

Certainly there's no physical reason for this requirement: the inner class can't hold an actual pointer into the method's stack frame. Indeed, the specification goes on to describe the mechanism that's actually used: the local variable is passed to the inner class constructor, so the inner class holds its own copy. Given that, the local variable could change without affecting the copy held by the class, and vice-versa.

But doing so would break the mental model of Java variables. On the one hand, there's the idea that local variables and member variables are equivalent in every way but scope. On the other, the idea that an inner class has the ability to change the member variables in its enclosing instance, and those changes will be seen by the enclosing instance as well as the inner class.

But allowing the inner class to change its copy of an enclosing method's local variable, without propagating that change to the enclosing method, would subtly break that model. Requiring the local variables to be declared final preserves the model at the cost of a needless bit of syntax. Is it that important?

To answer that, I'll ask a different question: have you ever climbed a staircase where one stair was not quite the same height as the others? Did you notice it? Did you trip?

When approaching a staircase, you unconsciously build a mental model of that staircase, then use that model to place your feet while climbing. I recall reading that most people place their foot within 1/16 of an inch of the expected height. This mental model is so strong, and the consequences potentially so expensive, that the International Residential Code (excerpted at stairways.org) specifies no more than 3/8 inch variance between the shortest and tallest stairs in a flight — about the width of your smallest fingernail.

The mental models that we build for programming are equally strong. They cover everything: the syntax and semantics of the language, the flow of data and code, the order of method parameters, and the One True Brace Style™. When you program in an environment where the models are consistent, you don't have to expend mental energy remembering how something works. When the models are inconsistent, particularly the semantics of the language, it's like placing your foot a half inch above the stair tread.

No comments: