Provide declarations for i and j that turn
this loop into an infinite loop:
while (i <= j && j <= i && i != j) { }
Solution 32: Curse of Looper
Oh, no, not another seemingly impossible looper! If i
<= j and j <= i, surely i must equal j?
This property certainly holds for the real numbers. In fact, it is so important
that it has a name: The
relation on
the real numbers is said to be antisymmetric.
Java's <= operator used to be antisymmetric before release 5.0, but
no longer.
Until release 5.0, Java's numerical
comparison operators (<, <=, >, and
>=) required both of their operands to be of a primitive numeric
type (byte, char, short, int, long,
float, or double) [JLS2 15.20.1]. In release 5.0, the
specification was changed to say that the type of each operand must be convertible to a primitive numeric type [JLS 15.20.1,
5.1.8]. Therein lies the rub.
In release 5.0, autoboxing and auto-unboxing were added to the
language. If you are unfamiliar with them see: http://java.sun.com/j2se/5.0/docs/guide/language/autoboxing.html
[Boxing]. The <=
operator is still antisymmetric on the set of primitive numeric values, but now
it applies to operands of boxed numeric types as
well. (The boxed numeric types are Byte, Character,
Short, Integer, Long, Float, and
Double.) The <= operator is not antisymmetric on operands
of these types, because Java's equality operators
(== and !=) perform reference identity comparison rather than
value comparison when applied to object references.
To make this concrete, the following declarations give the
expression (i <= j && j <= i && i != j) the value
true, turning the loop into an infinite loop:
Integer i = new Integer(0); Integer j = new Integer(0);
The first two subexpressions (i <= j and j
<= i) perform unboxing conversions [JLS
5.1.8] on i and j and compare the resulting int
values numerically. Both i and j represent 0, so both of these
subexpressions evaluate to TRue. The third subexpression (i !=
j) performs an identity comparison on the object references i and
j. The two variables refer to distinct objects, as each was initialized
to a new Integer instance. Therefore, the third subexpression also
evaluates to true, and the loop spins forever.
You might wonder why the language specification wasn't changed
to make the equality operators perform value comparisons when applied to boxed
numeric types. The answer is simple: compatibility. When a language is widely
used, it is unacceptable to change the behavior of existing programs in ways
that violate existing specifications. The following
program was always guaranteed to print false, and so it must
remain:
public class ReferenceComparison { public static void main(String[] args) { System.out.println(new Integer(0) == new Integer(0)); } }
The equality operators do perform numerical comparison when
only one of their two operands is of a boxed numeric type and the other is of a
primitive type. Because this was illegal prior to release 5.0, there are no
compatibility problems. To make this concrete, the following program was illegal
in release 1.4 and prints TRue in release 5.0:
public class ValueComparison { public static void main(String[] args) { System.out.println(new Integer(0) == 0); } }
In summary, there is a fundamental
difference in the way numerical comparison operators and equality operators
behave when both operands are of boxed numeric types: Numerical comparison
operators perform value comparisons, while equality operators perform reference
identity comparisons.
For language designers, life might have been simpler and more
pleasant if the equality operators had always performed value comparisons (Puzzle 13). Perhaps the
real lesson is that language designers should acquire high-quality crystal balls
in order to predict the future of the language and make all design decisions
accordingly. More seriously, designers should think about how the language might
evolve and should attempt to minimize constraints on evolution.
No comments:
Post a Comment
Your comments are welcome!