Monday, March 1, 2010

Dos Equis

Dos Equis

This puzzle tests your knowledge of the conditional operator, better known as the "question mark colon operator." What does the following program print?

public class DosEquis {

public static void main(String[] args) {

char x = 'X';

int i = 0;

System.out.print(true ? x : 0);

System.out.print(false ? i : x);

}

}






Solution : Dos Equis


The program consists of two variable declarations and two print statements. The first print statement evaluates the conditional expression (true ? x : 0) and prints the result. The result is the value of the char variable x, which is 'X'. The second print statement evaluates the conditional expression (false ? i : x) and prints the result. Again the result is the value of x, which is still 'X', so the program ought to print XX. If you ran the program, however, you found that it prints X88. This behavior seems strange. The first print statement prints X and the second prints 88. What accounts for their different behavior?



The answer lies in a dark corner of the specification for the conditional operator [JLS 15.25]. Note that the types of the second and third operands are different from each other in both of the conditional expressions: x is of type char, whereas 0 and i are both of type int. As mentioned in the solution to The joy of hex, mixed-type computation can be confusing. Nowhere is this more apparent than in conditional expressions. You might think that the result types of the two conditional expressions in this program would be identical, as their operand types are identical, though reversed, but it isn't so.



The rules for determining the result type of a conditional expression are too long and complex to reproduce in their entirety, but here are three key points.





  1. If the second and third operands have the same type, that is the type of the conditional expression. In other words, you can avoid the whole mess by steering clear of mixed-type computation.





  2. If one of the operands is of type T where T is byte, short, or char and the other operand is a constant expression of type int whose value is representable in type T, the type of the conditional expression is T.





  3. Otherwise, binary numeric promotion is applied to the operand types, and the type of the conditional expression is the promoted type of the second and third operands.





Points 2 and 3 are the key to this puzzle. In both of the two conditional expressions in the program, one operand is of type char and the other is of type int. In both expressions, the value of the int operand is 0, which is representable as a char. Only the int operand in the first expression, however, is constant (0); the int operand in the second expression is variable (i). Therefore, point 2 applies to the first expression and its return type is char. Point 3 applies to the second conditional expression, and its return type is the result of applying binary numeric promotion to int and char, which is int [JLS 5.6.2].



The type of the conditional expression determines which overloading of the print method is invoked. For the first expression, PrintStream.print(char) is invoked; for the second, PrintStream.print(int). The former overloading prints the value of the variable x as a Unicode character (X), whereas the latter prints it as a decimal integer (88). The mystery is solved.



Putting the final modifier on the declaration for i would turn i into a constant expression, causing the program to print XX, but it would still be confusing. To eliminate the confusion, it is best to change the type of i from int to char, avoiding the mixed-type computation.



In summary, it is generally best to use the same type for the second and third operands in conditional expressions. Otherwise, you and the readers of your program must have a thorough understanding of the complex specification for the behavior of these expressions.



For language designers, perhaps it is possible to design a conditional operator that sacrifices some flexibility for increased simplicity. For example, it might be reasonable to demand that the second and third operands be of the same type. Alternatively, the conditional operator could be defined without special treatment for constants. To make these alternatives more palatable to programmers, a syntax could be provided for expressing literals of all primitive types. This may be a good idea in its own right, as it adds to the consistency and completeness of the language and reduces the need for casts.

No comments:

Post a Comment