This program is
similar to the previous one, except this one is object-oriented. Learning from
our previous mistake, this version uses a general-purpose Map
implementation, a HashMap, in place of the previous program's
IdentityHashMap. What does this program print?
import java.util.*; public class MoreNames { private Map<String,String> m = new HashMap<String,String>(); public void MoreNames() { m.put("Mickey", "Mouse"); m.put("Mickey", "Mantle"); } public int size() { return m.size(); } public static void main(String args[]) { MoreNames moreNames = new MoreNames(); System.out.println(moreNames.size()); } }
Solution 63: More of the Same
This program
looks straightforward. The main method creates a MoreNames
instance by invoking the parameterless constructor. The MoreNames
instance contains a private Map field (m), which is
initialized to an empty HashMap. The parameterless constructor appears
to put two mappings into the map m, both with the same key
(Mickey). As we know from the previous puzzle, the ballplayer (Mickey
Mantle) should overwrite the rodent (Mickey Mouse), leaving a single mapping.
The main method then invokes the size method on the
MoreNames instance, which in turn invokes size on the map
m and returns the result, presumably 1. There's only one
problem with this analysis: The program prints 0, not 1.
What's wrong with the analysis?
The problem is that MoreNames has no
programmer-declared constructor. What it does have is a void-returning
instance method called MoreNames, which the author probably intended as
a constructor. Unfortunately, the presence of a return type (void)
turned the intended constructor declaration into a method declaration, and the
method never gets invoked. Because the MoreNames has no
programmer-declared constructor, the compiler helpfully (?) generates a public
parameterless constructor that does nothing beyond initializing the field of the
instance that it creates. As previously mentioned, m is initialized to
an empty HashMap. When the size method is invoked on this
HashMap, it returns 0, and that is what the program
prints.
Fixing the program is as simple as removing the void
return type from the Names declaration, which turns it from an instance
method declaration into a constructor declaration. With this change, the program
prints 1 as expected.
The lesson of this puzzle is: Don't
accidentally turn a constructor declaration into a method declaration by adding
a return type. Although it is legal for a method to have the same name as
the class in which it's declared, you should never do this. More generally,
obey the standard naming conventions, which
dictate that method names begin with lowercase letters, whereas class names
begin with uppercase letters.
For language designers, perhaps it was not such a good idea to
generate a default constructor automatically if no programmer-declared
constructor is provided. If such constructors are generated, perhaps they should
be private. There are several other approaches to eliminating this trap. One is
to prohibit method names that are the same as their class name, as does C#.
Another is to dispense with constructors altogether, as does Smalltalk.
No comments:
Post a Comment
Your comments are welcome!