Home > front end >  Abnormal method overloading result in Java
Abnormal method overloading result in Java

Time:04-29

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 a byte 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 a byte expression)

  • Otherwise, the promoted type is int, and all the expressions that are not of type int undergo widening primitive conversion to int.

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 type char or a constant expression of type int with a value that is representable in the type char, then the promoted type is char, and the int expressions undergo narrowing primitive conversion to char.

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.

  • Related