为什么Java 12会尝试将切换结果转换为数字?

Ily*_*lya 42 java switch-statement java-12

我同意以下代码:

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());
Run Code Online (Sandbox Code Playgroud)

返回此:

0
java.lang.Character
Run Code Online (Sandbox Code Playgroud)

但是,如果删除布尔值:

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());
Run Code Online (Sandbox Code Playgroud)

返回此:

48.0
java.lang.Float
Run Code Online (Sandbox Code Playgroud)

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

ern*_*t_k 47

根据switch表达式的JEP,switch表达式是一个poly表达式:

开关表达式是多边形表达式;如果目标类型已知,则将该类型推入每个手臂。开关表达式的类型是其目标类型(如果已知);如果不是,则通过组合每个案例分支的类型来计算独立类型。

由于您没有目标类型,因此不会检查表达式是否与任何给定类型匹配。

您可以通过替换var为以下类型来验证这一点:

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

在我的外壳中,此操作失败并显示:

|  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;
|                ^--^
Run Code Online (Sandbox Code Playgroud)

但是,如果您删除布尔值:...

足以了解如何确定独立类型(此处规则):

独立开关表达式的类型确定如下

  • 如果结果表达式都具有相同的类型(可能为null类型),则这就是switch表达式的类型。
  • 否则,如果每个结果表达式的类型是布尔值或布尔值,则将取消装箱转换(5.1.8)应用于每个布尔值类型的结果表达式,并且开关表达式的类型为布尔值。
  • 否则,如果每个结果表达式的类型都可以转换为数字类型(5.1.8),则switch表达式的类型是应用于结果表达式的数字提升(5.6)的结果。

  • 否则,将装箱转换(5.1.7)应用于具有原始类型的每个结果表达式,此后,switch表达式的类型是将捕获转换(5.1.10)应用于最小上限(4.10.4)的结果)类型的结果表达式。

据我所知,当删除布尔表达式时,剩下的是数字表达式(char '0'int 48)被提升为float 48.0)。请参阅上面的第三个要点。

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

  • 好答案。1+,这确实遵循了JLS中存在的二进制数值提升,直到添加了开关表达式为止。 (7认同)
  • @Ilya不,当您使用`var`时,没有目标类型。在这种情况下,必须将右侧视为独立表达式(不是多边形表达式),以确定表达式类型* then *,变量将获得结果类型。[正如安迪·特纳(Andy Turner)所说](/sf/ask/3870594591/## comment97317523_55294208),该行为与以前的Java版本一致。要点是,当类型不明显时,不要使用`var`声明变量。 (7认同)
  • @Ilya不,在第一个示例中,变量的类型是Object,Serializable和Comparable <?>的交集类型。如果要使用“对象”(如果以后要分配另一个值,这将是相关的),则必须显式声明它。您问题的示例等效于`var y = x == 0?'0':x == 1?0.0F:x == 2?2公升:x == 3?true:4;`。如前所述,仅在右侧明显时使用`var`。没有人会强迫您在其他地方使用它。 (6认同)
  • @AkashShah我相信这是因为没有发生类型转换。我尚未对此进行验证,但是我认为[最小上限](https://docs.oracle.com/javase/specs/jls/se11/html/jls-4.html#jls-4.10.4)所有这些表达式的类型都是“ java.lang.Object”。因此,它归结为类似“对象y = ...”之类的东西,结果的实际类型最终被打印出来(“ java.lang.Character”是匹配的case表达式的框式类型,返回“ 0” `) (5认同)