In a ternary statement typed like this: (boolean ?if type:else type), only the if type and the else type matters for the determination of the final type. The compiler deals with the situation as follows:
- If both the if type and else type are same, then the final type is also the same.
- If the if type can be converted to else type without loss of precision, the final type is the same as the else type.
- If the else type can be converted to if type without loss of precision, the final type is the same as the if type.
- If both 2 and 3 are possible, the final type is the same as the type of the lower data range.
- If none of the above qualify, the compiler throws a compilation error.*
Let us take these various versions:
System.out.println(true ? x : 0); //X
This becomes X because the if type is char, and the else type is any type that can represent 0. The constant 0 is valid in the char data range (0 to 65,535). It is possible to treat x and 0 as int, but the int data range is higher (-231 to 231-1).
As per point 4, compiler picks the type of lower range (char).
System.out.println(true ? x : i); //88
Here, the if type is a char, and the else type is an int. When you declare the variable as a non-final int, the compiler cannot be sure that the value will never change from 0 (even if your code doesn't change it anywhere!).
Hence only point 2 applies here. The if type char can be converted to else type int without any loss in precision (as int has higher range), but if the else type int is converted to the if type char, the lower range of char can cause loss of precision (the value of i could be outside the range of char).
Therefore the compiler picks int to avoid loss of precision, as per point 2.
Some other test cases (similar reasons as already explained above):
System.out.println(false ? i : x); //88 (as per point 3) System.out.println(false ? 0 : x); //X (as per point 4)
However, in this code, the result is different:
final int i = 0; System.out.println(true ? x : i); //X System.out.println(false ? i : x); //X
Can you tell why?
* Note: All types can ultimately convert to Object with no loss, including primitive types which can be auto-boxed (on recent Java versions). If the method is overloaded to accept Object type, you may never experience this case as the compiler converts the final type to Object.
System.out.printmethods that will be called in each case (including parameter types)?System.out.printa method withcharis getting called and for second caseintis being called. this what the strange thing not understanding why the secondprintcalls withintdata type not withchardata type?a ? b : c?charholds a UTF-16 code-unit. In UTF-16, "X" happens to be encoded by one code-unit. So, 'X' fits into achar(88, in decimal). No need to continue on to ASCII.