Java 12为什么尝试将switch的结果转换为数字?

47

我同意这段代码:

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

返回此结果:

0
java.lang.Character

但是如果您删除布尔值:

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

返回这个:

48.0
java.lang.Float

我想这个结果是出乎意料的。


可能是某种优化。在第二个例子中,你可以将所有结果映射成Float,而在第一个例子中,它们只是Object。 - Ralf Renz
14
我想它的原因与 true ? '0' : false 返回字符一样,是因为它需要进行装箱操作,而 true ? '0' : 0.0f 返回浮点数是因为会发生二进制数字升级。 - Andy Turner
3
有人能指出语言规范中定义它们的部分吗?我找不到"switch expression"被提及的地方。链接 - Andy Turner
1个回答

53
根据JEP 325,switch表达式是一种多态表达式:
一个switch表达式是一种多态表达式;如果目标类型已知,则将该类型推入每个分支中。如果已知switch表达式的目标类型,则其类型为目标类型;否则,通过组合每个case分支的类型来计算独立的类型。
因为你没有目标类型,所以不会检查表达式是否与任何给定类型匹配,这是可以预料的。
你可以通过用类型替换var来验证这一点:
int y = switch (0) {
    case 0 -> '0';
    case 1 -> 0.0F;
    case 2 -> 2L;
    case 3 -> true;
    default -> 4;
};

在我的Shell中,这会失败:

|  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;
|                ^--^

但是如果您移除boolean:...。就足以看出独立类型是如何确定的(规则在这里):
独立开关表达式的类型如下确定: - 如果结果表达式的类型都相同(可以是空类型),则该开关表达式的类型即为该类型。 - 否则,如果每个结果表达式的类型为 boolean 或 Boolean,则对类型为 Boolean 的每个结果表达式应用拆箱转换(5.1.8),并且开关表达式的类型为 boolean。 - 否则,如果每个结果表达式的类型均可转换为数值类型(5.1.8),则应用数值促进(5.6)后,开关表达式的类型为结果表达式的类型。 - 否则,在具有原始类型的每个结果表达式上应用装箱转换(5.1.7),之后将捕获转换应用于结果表达式类型的最小上界(4.10.4),开关表达式的类型即为结果表达式的类型。
据我所见,当您删除布尔表达式时,剩下的是数字表达式(char '0'(int 48)会提升为float 48.0)。请参见上面的第三个要点。

至于为什么结果的类型是float,请参见数字上下文部分。


9
好的回答。1+,这确实遵循了JLS中存在的二进制数值提升规则,直到添加switch表达式为止。 - Eugene
6
我认为这是因为没有进行类型转换。我没有验证过,但我认为所有这些表达式类型的最小上界都是java.lang.Object。所以它归结为类似于Object y = ...的东西,结果的实际类型最终会被打印出来(匹配的 case 表达式的装箱类型为java.lang.Character,返回值为'0')。 - ernest_k
1
@Ilya 可能是我表达不够准确。但我在这里指的是一个独立表达式和多态表达式之间的区别(使用var和使用int版本之间的区别是什么?这就是我所说的)。通过阅读第5章:转换和上下文,您可以更好地了解。 - ernest_k
8
不,当你使用 var 时,没有目标类型。在这种情况下,右侧必须被视为独立的表达式(而不是多态表达式),以确定表达式类型,然后变量将获得结果类型。正如 Andy Turner 所说,这种行为与之前的 Java 版本保持一致。结论是,当类型不明显时,不要使用 var 来声明变量。 - Holger
7
在第一个例子中,变量的类型是ObjectSerializableComparable<?>的交集类型。如果您想要Object(如果您想在以后的某个时间点分配另一个值会很相关),您必须明确声明它。你提出的问题的例子等价于var y = x == 0? '0': x == 1? 0.0F: x == 2? 2L: x == 3? true: 4;。正如所说的那样,只有当右侧显而易见时才使用var。没有人强迫你在其他地方使用它。 - Holger
显示剩余4条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接