Java Reference
In-Depth Information
Solution 23: No Pain, No Gain
At first glance, this program might appear to print out the words
Pain
,
Gain
, and
Main
with equal
likelihood, varying from run to run. It appears to choose the first letter of the word, depending on
the value chosen by the random number generator:
M
for 0,
P
for 1, and
G
for 2. The puzzle's title
might have provided you with a clue that it doesn't actually print
Pain
or
Gain
. Perhaps more
surprisingly, it doesn't print
Main
either, and its behavior doesn't vary from run to run. It always
prints
ain
.
Three bugs conspire to cause this behavior. Did you spot them all? The first bug is that the random
number is chosen so the
switch
statement can reach only two of its three cases. The specification
for
Random.nextInt(int)
says: "Returns a pseudorandom, uniformly distributed
int
value between
0
(inclusive) and the specified value (exclusive)"
[Java-API]
. This means that the only possible
values of the expression
rnd.nextInt(2)
are
0
and
1
. The
switch
statement will never branch to
case
2
, which suggests that the program will never print
Gain
. The parameter to
nextInt
should
have been
3
rather than
2
.
This is a fairly common source of problems, known as a
fencepost error
. The name comes from the
common but incorrect answer of 10 to the question, If you build a fence 100 feet long with posts 10
feet apart, how many posts do you need? Both 11 and 9 are correct answers, depending on whether
there are posts at the ends of the fence, but 10 is wrong.
Watch out for fencepost errors.
Whenever you are working with lengths, ranges, or moduli, be careful to determine which
endpoints should be included, and make sure that your code behaves accordingly.
The second bug is that there are no
break
statements between the cases. Whatever the value of the
switch
expression, the program will execute that case and all subsequent cases [JLS 14.11]. Each
case assigns a value to the variable
word
, and the last assignment wins. The last assignment will
always be the one in the final case (
default
), which is
new StringBuffer('M')
. This suggests that
the program will never print
Pain
or
Gain
but always
Main
.
The absence of
break
statements in
switch
cases is a common error that tools can help you catch.
As of release 5.0,
javac
provides the
-Xlint:fallthrough
flag to generate warnings when you
forget a
break
between one case and the next.
Don't fall through from one nonempty case to
another.
It's bad style because it's unusual and therefore confusing to the reader. Nine times out of
ten, it indicates an error. If Java had not been modeled after
C
, its
switch
statement would probably
not require breaks. The lesson for language designers is to consider providing a structured
switch
statement.
Search WWH ::
Custom Search