我的上一篇文章“ 玩JDK 12的Switch表达式 ”讨论了如何使用JDK 12 Early Access Builds尝试JEP 325 switch
表达式和语句,并提供了一个简单的示例。 这篇文章使用相同的JDK 12 Early Access Build 10来演示switch
表达式和增强的switch
语句的不同功能。
我在博客文章“ 通过引入switch表达式来增强Java switch语句 ”中使用了2×2网格,以说明新的“箭头”语法(“ switch标为规则”)可以与switch
语句或switch
表达式一起使用。按照JEP325。类似地,按照JEP 325,传统的“冒号”语法(“带开关标记的语句组”)也可以与switch
表达式或switch
语句一起使用。 换句话说,结肠的存在( :
) 并不一定意味着一个switch
的“箭头”的声明 ,并存在( ->
) 并不一定意味着一个switch
表达 。 为了方便起见,我在前面的文章中提供了表格的改编版本。
声明 (“交换机的非本地控制流_out_ [继续到封闭循环,带有标签的中断,返回]”) | 表达 (总计:返回一个值) | |
---|---|---|
开关标签声明组 (“结肠”) (启用穿透) | switch 我们知道和“爱”,但增强了 | break 返回的值类似于return |
开关标签规则 (“箭头”) (防止掉线) | 语句/冒号的“语法简写”(上)以及
| 箭头( -> )指向返回值 |
使用JDK 12 Early Access Build ,可以方便地试用新的switch
表达式 ,我们还可以试用传统的和增强的switch
语句版本。
传统开关声明
即使启用了JDK 12预览( --enable-preview
),我们仍然可以使用我们“了解并爱着”的传统switch
语句 。 下面显示了这个传统 switch
语句的示例,即使启用了JDK 12语言功能预览,该语句也可以成功编译和执行。
/**
* Demonstrate traditional switch statement assigned to
* local variable.
*/
public static void demonstrateTraditionalSwitchStatement()
{
out.println("Traditional Switch Statement:");
final int integer = 3;
String numericString;
switch (integer)
{
case 1 :
numericString = "one";
break;
case 2 :
numericString = "two";
break;
case 3:
numericString = "three";
break;
default:
numericString = "N/A";
}
out.println("\t" + integer + " ==> " + numericString);
}
这篇文章中显示的此代码以及所有其他代码示例可在GitHub上找到 。 这个特定的示例显示了传统switch
语句设置局部变量值的常见用法。 我之所以选择此用例,是因为新的switch
表达式是实现此目的的一种改进方法。
增强型开关声明
如前所述,我们可以在增强的 switch
语句中使用新的“箭头”语法(“带有开关标签的规则”)。 在使用--enalved-preview
的JDK 12 Early Access Build 10上编译并运行的下一个代码示例中显示了这一点。
/**
* Demonstrate enhanced switch statement used to assign
* a local variable.
*/
public static void demonstrateEnhancedSwitchStatement()
{
out.println("Enhanced Switch Statement:");
final int integer = 2;
String numericString;
switch (integer)
{
case 1 -> numericString = "one";
case 2 -> numericString = "two";
case 3 -> numericString = "three";
default -> numericString = "N/A";
}
out.println("\t" + integer + " ==> " + numericString);
}
最后一个示例显示了该switch
仍然用作语句 ,但是在这种情况下,它利用了“箭头”语法(“标签规则”)来完成其切换,而没有明确指定break
。 这不仅减少了代码量,而且更重要的是具有不允许经常令人恐惧的switch
“掉线”的优点。 简而言之, 增强后的 switch
语句的工作方式类似于当前/传统的switch
语句,但是没有传统版本的潜在缺陷。
新开关表达式通过中断返回值
JEP 325除了增强当前的switch
语句以允许指定switch
语句而没有掉线的风险之外,还引入了在switch
表达式中使用switch
关键字的概念。 Java教程的“ 表达式,语句和块 ”页面介绍了语句和操作之间的区别。 为了便于讨论,该教程中的两个重要观察结果是(我加粗了强调):
- “ 表达式是由变量,运算符和方法调用组成的结构, 其结果为单个值 。”
- “ Java编程语言允许您从各种较小的表达式中构造复合表达式 ,只要该表达式的一部分所需的数据类型与另一部分的数据类型匹配即可。”
下一个代码清单演示了如何使用JDK 12 Early Access Build 10和--enable-preview
替换上面显示的代码,该代码使用switch
语句将一个值分配给较早声明的局部变量,而该语句使用一个在单个语句中switch
表达式以将其结果值分配给局部变量。
/**
* Demonstrate switch expression using colons and breaks.
*/
public static void demonstrateSwitchExpressionWithBreaks()
{
final int integer = 1;
out.println("Switch Expression with Colons/Breaks:");
final String numericString =
switch (integer)
{
case 1 :
break "uno";
case 2 :
break "dos";
case 3 :
break "tres";
default :
break "N/A";
};
out.println("\t" + integer + " ==> " + numericString);
}
刚刚显示的代码示例演示了switch
表达式的使用,该表达式与前面显示的传统switch
语句示例非常相似。 但是,有几个明显的不同。 区别在于此switch
表达式返回的结果分配给局部变量“ numericString
”。 第二个区别直接与switch
表达式能够返回一个值有关,在于break
子句现在每个都有要在break
关键字后立即指定的相关case
要返回的值。 本质上, switch
表达式的break
就像Java方法return
。
通过标签规则的新开关表达式返回值
只是示出的实施例表明,人们可以从一个返回值switch
有类似结肠表达式 ( :
)和break
语法什么一个很可能与使用switch
的语句。 除了熟悉之外,此方法的另一个优点是可以在返回单个值之前为单个case
指定多个语句。 但是,在大多数情况下,使用前面讨论的“箭头”语法从switch
表达式返回值可能会变得很流行,因为它不会遭受掉线的风险,并且可以避免通常与传统switch
语句相关的范围意外。 下一个代码清单演示了新的switch
表达式如何使用“标签规则”(“箭头”语法)而不是冒号并break
以优雅地返回该switch
的单个解析值。
/**
* Demonstrate switch expressions using "arrow" syntax.
*/
public static void demonstrateSwitchExpressionWithArrows()
{
final int integer = 4;
out.println("Switch Expression with Arrows:");
final String numericString =
switch (integer)
{
case 1 -> "uno";
case 2 -> "dos";
case 3 -> "tres";
case 4 -> "quatro";
default -> "N/A";
};
out.println("\t" + integer + " ==> " + numericString);
}
上面的四个示例演示了2×2网格中所示的每种情况。 本文的其余部分将讨论使用JDK 12 Early Access Build 10尝试switch
表达式和语句时的一些其他观察结果。
可以为单个情况指定多个常量
2×2网格中的四个象限中的任何一个都允许将多个常数与单个case
相关联。 在下一个代码清单中对此进行了演示,该清单在启用“预览语言功能”的情况下与JDK 12 Early Access Build 10一起编译和运行。
/**
* Demonstrate that multiple constants can be associated with
* a single {@code case} and used in conjunction with a
* {@code switch} expression that uses the "arrow" syntax.
*/
public static void demonstrateLabelRulesWithSharedCases()
{
final int integer = 7;
out.println("Multiple Case Labels:");
final String numericString =
switch (integer)
{
case 0 -> "zero";
case 1, 3, 5, 7, 9 -> "odd";
case 2, 4, 6, 8, 10 -> "even";
default -> "N/A";
};
out.println("\t" + integer + " ==> " + numericString);
}
/**
* Demonstrate that multiple constants can be associated with
* a single {@code case} and used in conjunction with a
* {@code switch} statement that uses the traditional colon and
* {@code break} syntax.
*/
public static void demonstrateBlockedStatementsWithSharedCases()
{
final int integer = 6;
out.println("Multiple Case Labels:");
String numericString;
switch (integer)
{
case 0:
numericString = "zero";
break;
case 1, 3, 5, 7, 9:
numericString = "odd";
break;
case 2, 4, 6, 8, 10:
numericString = "even";
break;
default:
numericString = "N/A";
};
out.println("\t" + integer + " ==> " + numericString);
}
不能混用“箭头”(“标签规则”)和冒号/中断(“语句组”)
JDK 12 Early Access Build 10编译器( javac
)不允许混合使用“箭头”语法和传统的冒号/ break
语法。 尝试将这些混合使用会导致错误消息:“ 错误:开关中使用的种类不同 ”。 接下来显示了一个示例代码,该代码无法编译并显示此特定错误消息。
/**
* WARNING - This does NOT compile, even with JDK 12 Early
* Access Builds and --enable-preview because JEP 325 does
* not allow the "arrow" syntax to be mixed with the
* traditional colon/break syntax.
*/
public static void demonstrateMixed()
{
final int integer = 3;
String numericString;
switch(integer)
{
case 1 :
numericString = "one";
break;
case 2 -> numericString = "two";
default -> numericString = "N/A";
}
return numericString;
}
Switch语句的中断无法返回值
新的switch
表达式返回一个值,当switch
表达式使用冒号和break
方法时,将在break
关键字之后立即指定该返回值。 因为传统的switch
语句不返回值,所以尝试使与switch
语句关联的break
指定返回值是编译时错误。 错误(“错误:意外的值中断”)可以用以下代码重现。
/**
* WARNING - This does NOT compile, even with JDK 12 Early
* Access Builds and --enable-preview because it is
* nonsensical to have a "statement" return a value; that
* is what an expression should be used for.
*/
public static void demonstrateSwitchStatementReturnedLabel()
{
final int integer = 4;
switch (integer)
{
case 1:
break "one";
case 2:
break "two";
case 3:
break "three";
default:
break "N/A";
};
}
当尝试使用带有指定标志--enable-preview
和-release 12
JDK 12 Early Access Build 10的javac
编译器来编译以上代码时,错误消息“ error:意料之外 ”的四个实例(对应于三种case
加上一种default
) 价值突破 ”可见。 毫不奇怪,将switch
分配给局部变量(并有效地将语句转换为表达式 )的简单更改就可以编译该代码。 换句话说,将上面的代码更改为下一个代码清单中的代码可以使其编译并成功运行。
/**
* This demonstrates that a {@code switch} "expression" is
* able to (and expected to) provide the "return" value for
* a given {@code case} and {@code default} instead of being
* a compiler error as it was for the "statement" example
* demonstrated in method
* {@link #demonstrateSwitchStatementReturnedLabel()}.
*/
public static void demonstrateSwitchExpressReturnedLabel()
{
final int integer = 4;
final String numericString =
switch (integer)
{
case 1:
break "one";
case 2:
break "two";
case 3:
break "three";
default:
break "N/A";
};
}
当前的JEP 325文本包含有关此break
行为与方法return
相似的讨论。 该讨论指出, switch
语句在其break
s之后不需要返回值的情况类似于返回void
的方法。 switch
表达式应返回非void
值。
Switch语句的“箭头”语法必须指向一条语句
即使向javac
编译器提供了--enable-preview
和-release 12
,以下代码也无法在JDK 12 Early Access Build 10中进行编译。
/**
* WARNING - This does not compile, even with JDK 12 Early
* Access Builds and --enable-preview and reports error message
* "error: not a statement" because it is expecting a
* {@code switch} "statement" but what is being provided to each
* {@code case} is NOT a statement.
*/
public static void demonstrateSwitchStatementReturnedValueViaLabelRule()
{
final int integer = 5;
switch (integer)
{
case 1 -> "one";
case 2 -> "two";
};
out.println(numericString);
}
上面的代码无法编译,并且报告的错误消息是“错误:不是语句”。 这是因为在此示例中将switch
用作语句,但是“箭头”语法是“指向”文字字符串而不是有效的Java语句。
所有可能性都必须在switch表达式中指定
因为switch
表达式需要返回一个非void
值,所以switch
表达式必须为其可能打开的所有可能值指定case
。 实际上,这很可能是通过default
来实现的,以捕获未用case
明确指定的所有可能性。 使用传统的switch
语句,不需要确保所有可能打开的值都由case
或default
值覆盖,并且有时会导致某些情况,例如我在博客文章“ Log Unexpected Switch Options ”中所述。
以下代码违反了以下规则: switch
表达式必须指定case
或default
值的所有可能值:
/**
* WARNING - This method will not compile even with JDK 12
* Early Access Build 10 with --enable-preview because of
* error; "the switch expression does not cover all possible
* input values".
*/
public static void demonstrateLackingCaseInSwitchExpression()
{
final int integer = 5;
String numericString =
switch (integer)
{
case 1 -> "one";
case 2 -> "two";
};
out.println(numericString);
}
刚刚显示的代码将无法编译,并且因果错误消息为“错误:开关表达式未涵盖所有可能的输入值。”
JEP 325对交换机未来使用的影响
考虑到Java中除了switch
语句之外还有可用的switch
表达式所带来的可能性,并考虑到可以与switch
表达式或语句一起使用的新“箭头”语法所提供的优势,开始思考何时进行有趣的事情。上面2×2网格中的每个象限最有利。 总的来说,我相信我会发现自己经常使用带有“箭头”语法(“标签规则”)的switch
表达式 ,同时也经常使用带有“ arrow”语法的增强型switch
语句 。 我怀疑将来我会很少使用传统的:
( break
)语法。 即使在特定case
有多个语句要执行时,我也可能会将这些语句分解为单个方法,在这种case
使用“箭头”语法进行调用。 这将使我受益于更明显的作用域并避免掉线的风险。 鉴于现在可以为单个case
指定多个常量,即使在多个案例导致相同结果的情况下,也不再需要穿透。
其他资源
- JEP 325:开关表达式(预览)
- JEP 325规范:开关表达式
- JDK 12抢先体验版
- JDK语言功能预览:切换表达式
- 玩JDK 12的Switch表达式
- 通过引入switch表达式来增强Java switch语句
- 切换表达式到Java吗?
- 切换表达式-收集线程
- LJC演讲的少量反馈
- GitHub上这篇文章的示例
翻译自: https://www.javacodegeeks.com/2018/09/jdk-12-switch-statements-expressions.html