Why does Java 12 try to convert the result of a switch to a number?

I agree that this code:

var y = switch (0) {
    case 0 -> '0';
    case 1 -> 0.0F;
    case 2 -> 2L;
    case 3 -> true;
    default -> 4;
};
System.out.println(y);
System.out.println(((Object) y).getClass().getName());

returns this:

0
java.lang.Character

But if you remove boolean:

var y = switch (0) {
    case 0 -> '0';
    case 1 -> 0.0F;
    case 2 -> 2L;
    default -> 4;
};
System.out.println(y);
System.out.println(((Object) y).getClass().getName());

returns this:

48.0
java.lang.Float

I suppose this result is unexpected.


Solution 1:

According to the switch expression's JEP, a switch expression is a poly expression:

A switch expression is a poly expression; if the target type is known, this type is pushed down into each arm. The type of a switch expression is its target type, if known; if not, a standalone type is computed by combining the types of each case arm.

Because you don't have a target type, the expression is not checked to match any given type, which is expected.

You can verify this by replacing var with a type:

int y = switch (0) {
    case 0 -> '0';
    case 1 -> 0.0F;
    case 2 -> 2L;
    case 3 -> true;
    default -> 4;
};

In my shell, this fails with:

|  Error:
|  incompatible types: bad type in switch expression
|      possible lossy conversion from float to int
|      case 1 -> 0.0F;
|                ^--^
|  Error:
|  incompatible types: bad type in switch expression
|      possible lossy conversion from long to int
|      case 2 -> 2L;
|                ^^
|  Error:
|  incompatible types: bad type in switch expression
|      boolean cannot be converted to int
|      case 3 -> true;
|                ^--^

But if you remove boolean:...

It should be enough to see how the standalone type is determined (rules here):

The type of a standalone switch expression is determined as follows:

  • If the result expressions all have the same type (which may be the null type), then that is the type of the switch expression.

  • Otherwise, if the type of each result expression is boolean or Boolean, an unboxing conversion (5.1.8) is applied to each result expression of type Boolean, and the switch expression has type boolean.

  • Otherwise, if the type of each result expression is convertible to a numeric type (5.1.8), the type of the switch expression is the result of numeric promotion (5.6) applied to the result expressions.

  • Otherwise, boxing conversion (5.1.7) is applied to each result expression that has a primitive type, after which the type of the switch expression is the result of applying capture conversion (5.1.10) to the least upper bound (4.10.4) of the types of the result expressions.

As far as I can see, when you remove the boolean expression, you're left with numeric expressions (char '0' (int 48) is promoted to float 48.0). See third bullet point above.

And as for why float is the result's type, see the Numeric Contexts section.