Could you explain in a detailed manner why the expected result is not correct? As most of the readers expect that the output is Byte Char Int Byte
, but of course it is not the correct answer.
public class Tester {
public static String test(byte b) {
return "Byte ";
}
public static String test(char c) {
return "Char ";
}
public static String test(int i) {
return "Int ";
}
public static void main(String[] args) {
byte b = 0;
char c = 'A';
System.out.print(test(true ? b : c));
System.out.print(test(false ? b : c));
System.out.print(test(true ? 0 : 'A'));
System.out.print(test(false ? 'A' : (byte)0));
}
}
The correct result is Int Int Char Int
.
CodePudding user response:
This is because overload resolution depends on the compile-time type of the argument expressions, not the runtime type of the argument. See JLS 15.12.2.2:
The argument expression true ? b : c
has the type int
, so the Int
method is called. This is specified in the JLS 15.25.2, where it is said that if none of the special cases match (none of them do here), then the type of the conditional expression is the type after applying numeric promotion to the second and third operands:
An expression appears in a numeric choice context if the expression is one of the following:
- The second or third operand of a numeric conditional expression
- [...]
Numeric promotion determines the promoted type of all the expressions in a numeric context. [...] The rules are as follows:
[...] (none of these rules match in the case of a
short
and abyte
expression)Otherwise, none of the expressions are of type double, float, or long. In this case, the kind of context determines how the promoted type is chosen.
In a numeric choice context, the following rules apply:
[...] (none of these rules match in the case of a
short
and abyte
expression)Otherwise, the promoted type is
int
, and all the expressions that are not of typeint
undergo widening primitive conversion toint
.
This is why true ? b : c
is of type int
. The same goes for false ? b : c
and false ? 'A' : (byte)0
. It doesn't matter what the condition is. The type of the expression is determined by the types of the second and third operands.
As for test(true ? 0 : 'A')
, this can be explained by a rule that I omitted above, just before the last "Otherwise..."
- Otherwise, if any expression is of type
char
, and every other expression is either of typechar
or a constant expression of typeint
with a value that is representable in the typechar
, then the promoted type ischar
, and theint
expressions undergo narrowing primitive conversion tochar
.
In this case, the expression "0
" is a "a constant expression of type int
with a value that is representable in the type char
".
One way to think about the type of a conditional expression, is that it's the smallest type that the expressions of both of the branches can "fit". For a byte b
and a char c
, int
is the smallest type that can fit both of them. For 0
, and 'A'
, char
is enough to fit both, as it is known that 0
is in the range of char
.
CodePudding user response:
The ternary operator converts its expressions to a common type in order for a variable to store its result.
Imagine if you had an expression like this:
char v = <myCondition> ? 'a' : 0L;
The previous statement would make sense only if the condition was true, but what about if this was false and 0L was to be returned? long
and char
are not the same data type. So, the compiler needs to find a common type, without losing precision, in order to allow both expressions to be returned and assigned to a variable suitable for both of them. The resulting data type is picked up regardless the condition's outcome.
In this case, long
cannot be cast to int
and then char
as it would lose precision whereas char
could be cast to int
and then promoted to long
. This is why the previous statement can be correctly written like so:
long v = <myCondition> ? 'a' : 0L;
In your example, the first ternary operator has a byte
and a char
, a byte
can be promoted to an int
while a char
can be cast to an int
. The other way around, where a char
would be cast to a byte
is not possible as this would cause a precision loss (a char
occupies two bytes). By applying the same logic of promotion, you can also explain the remaining cases. Except for the third one, where you should apply the resolving table from the previously provided link to the documentation.
CodePudding user response:
For the type of a conditional expression in Java, always consult the JLS charts (Section 15.25).
3rd → byte short char int 2nd ↓ byte byte short bnp(byte,char) byte | bnp(byte,int) Byte byte short bnp(Byte,char) byte | bnp(Byte,int) short short short bnp(short,char) short | bnp(short,int) Short short short bnp(Short,char) short | bnp(Short,int) char bnp(char,byte) bnp(char,short) char char | bnp(char,int)
where the notation "bnp(x, y)" means the type that is the result of binary numeric promotion of x and y, and the form "T | bnp(..)" means that if one operand is a constant expression of type int
and may be representable in type T, then binary numeric promotion is used if the operand is not representable in type T.
For the first 2 calls, we have a char
and a byte
, so the result is "bnp(byte, char)", or the type int
.
For the third call, we have a constant expression of type int
and a char
, and the value is representable as a char
(it's 0
), so the result is the type char
.
For the fourth call, we have a byte
and a char
, so we back to "bnp(byte,char)", which is the type int
.
CodePudding user response:
Table 15.25-A in the Java language specification describes what type the result of a ?:
operator is, depending on the contributing types.
Your first example involves byte
and char
, so the result is a bnp (binary numeric promotion) of these, and for anything but double
, float
, or long
, the resulting type is int
.
Same thing for the others.