Tuesday, 18 September 2012

Puzzle 32: Curse of Looper


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!