简体   繁体   中英

Why is the following Java program giving strange output?

I found below puzzle in Java Puzzlers,

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); 
    }
}

I tried this code and run it as well but output was not came as per my guess,

my guess output should be : XX but in
reality output is : X88

I tried lot to understand but i couldn't,can anybody give us the explanation? why is the different output is coming ? since i was able to understood that first print() that will print variable char x character value, but second print() printing 88 an ASCII- representation of value in char x .if i simplify the ternary operators expression in second print() like this

if(false){
    System.out.print(i);
}else{
    System.out.print(x);
}

then output is coming XX ,quite strange, can anybody lighten this problem?

It would be great help for me to understand ternary operators.

Thanks in advance!

The reason for this behavior is that a ternary has one result type that the compiler has to choose in advance, and the flavor of print called in response.

In the true ? x : 0 true ? x : 0 case, 0 is treated as a char value, and print(char) is invoked. In the second case, since i is an int , x is also implicitly cast to int (a widening cast) and print(int) is invoked, yielding numeric output. The opposite, casting i to char implicitly, is illegal, because it could lose precision.

The implications of statically resolving the type can be shown with this example - not with print , because there is a print(Object) , but consider this:

void method(boolean b);
void method(Integer i);
...
method(cond? false:0);

No matter what cond is, there is an overload compatible with the parameter. However, the compiler needs to chose one overload, which is not possible at compile time. The compiler will autobox both and assign the expression as Object *, but there is no method(Object) .


*actually, my compiler said "The method method(boolean) in the type Test is not applicable for the arguments ( Object&Serializable&Comparable<?> )", but the point stands.

Whatever result you are seeing is in accordance with the rules specified in JLS 15.25 for determining the type of conditional operation. Here is the point that it mentions:

If the second and third operands have types that are convertible (§5.1.8) to numeric types, then there are several cases:

  • If one of the operands is of type byte or Byte and the other is of type short or Short, then the type of the conditional expression is short.

  • If one of the operands is of type T where T is byte, short, or char, and the other operand is a constant expression (§15.28) of type int whose value is representable in type T, then the type of the conditional expression is T.

  • If one of the operands is of type T, where T is Byte, Short, or Character, and the other operand is a constant expression (§15.28) of type int whose value is representable in the type U which is the result of applying unboxing conversion to T, then the type of the conditional expression is U.

  • Otherwise, binary numeric promotion (§5.6.2) is applied to the operand types, and the type of the conditional expression is the promoted type of the second and third operands.

In your question, the last point is followed. x is char type while i is of int type , so x is promoted to int . Hence the output for the second line is 88 which is the int conversion of char X .

To understand what is happening here, we need to track the types. Lets consider each print statement in turn, first:

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

Here the types are boolean ? char : const int

The compiler will want the ternary statement to return a single type, it cannot return either a char or an int. It spots that 0 can be a converted to char constant, and so treats it as the expression as 'boolean ? char : char'. Where the last char has the value zero. Because the boolean was true, the first char is printed as an X.

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

Here the types are boolean ? int : char, the int is an int variable and can not be treated as a char so the choice for the compiler is to either error as the types are different, narrow the int or widen the char. Narrowing the int would loose precision which, and so the compiler would not do it behind our back (it would require an explicit cast). Widening does not loose precision so the char is converted to an int. The int value of 'X' is 88. Thus the value 88 is printed.

More details of this conversion process can be read here: Java Language Spec

In this line :

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

X is casted to int, that's way prints the decimal value of char X

When you use a ternary operator condition?valTrue:valFalse . This will return the same datatype in both the case (true or false) . in line System.out.print(condition? i : x); you are aspecting int if true and Char if false , but it will return only int in both the case . similarly in line System.out.print(condition? x : i); in bothe case it will return Char .

In the first expression one side is a char, and 0 is assignable to a char ; hence the first ternary expression is a char. char zero = 0;

In the second there are int and char, and the char is widened : (int)'X' is 88.

Constants like in c ? "" : null c ? "" : null hence do not make an Object but a String (here).

Your observation is correct. But the ternary operator deal with the same data type. and it consider the data type of true vale and convert the type of false value if required. In your case at first print statement the ternary operator will consider data type as Char as the data type of x and at second print statement the ternary operator will consider data type as int as the data type of i that's the reason you are getting ASCII- representation of value in char x.

    System.out.print(true  ? x : 0);
 /*ternary operator(compiler) consider the resulting data 
   type as char as the true Value type is char.*/
    System.out.print(false ? i : x); 
/*ternary operator(compiler) consider the resulting data 
   type as int as the true Value type is int.*/

/* but if you replace the print statement with if else condition 
 then there is no point of ternary concept at all*/

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:

  1. If both the if type and else type are same, then the final type is also the same.
  2. If the if type can be converted to else type without loss of precision, the final type is the same as the else type .
  3. If the else type can be converted to if type without loss of precision, the final type is the same as the if type .
  4. If both 2 and 3 are possible, the final type is the same as the type of the lower data range.
  5. 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 (-2 31 to 2 31 -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 .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM