正如我在“ 玩JDK 12的Switch表达式 ”一文中所写的那样, JDK 12 Early Access Build使JEP 325的实现(“ Switch Expressions(Preview)”)的实验变得容易。 我的帖子“ JDK 12:实际中的切换语句/表达式 ”使用代码示例来演示增强的switch
语句和新的switch
表达式的核心特征。 在本文中,我将介绍在JEP 325中显式调用的一种特殊情况,该特殊情况与在运行时添加到switch
表达式中的枚举有关。
因为switch
表达式返回一个值,所以必须通过case
处理该switch
可能遇到的所有可能的case
(或者对于那些未与case
明确关联的case
default
将其覆盖)。 JEP 325规定如下:
switch表达式的情况必须详尽无遗; 对于任何可能的值,必须有一个匹配的开关标签。 实际上,这通常仅意味着需要一个默认子句。 但是,在枚举开关表达式涵盖所有已知情况的情况下(并最终在密封类型上切换开关表达式),编译器可以插入默认子句,该子句指示枚举定义在编译时和运行时之间已更改。 (这是开发人员今天手动执行的操作,但是与手工编写的消息相比,让编译器插入它既不那么具有侵入性,又可能具有更具描述性的错误消息。)
我已经写了类似于JEP 325中描述的代码(“这就是开发人员今天要做的事情”),正如我在博客文章“ 记录意外的开关选项 ”中所讨论的那样。 过去,通常明智的做法是添加逻辑来处理或记录未在default
显式调用或处理的switch
语句选项。 随着通过JDK 12和JEP 325的switch
表达式的出现,现在是必需的。
JEP 325解决了枚举上的switch
表达式的情况,并明确指定了在编译带有switch
表达式的enum和代码的case
子句中显式指定所有enum的值时如何支持情况的方法,但后来又添加了更多的值到枚举,而无需使用该枚举重新编译switch
表达式代码。
为了展示这种支持,我将提供一个简单的枚举以及两个基于JEP 325和JDK Early Access Build 10的示例,以便在switch
语句和switch
表达式中使用该枚举。
下面的代码清单显示了一个名为Response
的简单枚举,它只有两个值。
package dustin.examples.jdk12.switchexp;/*** Enum representation of a response.*/
public enum Response
{YES,NO;
}
下一个代码清单显示了一个类,其中包括使用上述枚举的两个方法。 一种方法针对该枚举使用switch
语句 ,另一种方法针对该枚举使用switch
表达式 。
package dustin.examples.jdk12.switchexp;import static java.lang.System.out;/*** Demonstrates implicit handling of expanding enum* definition related to JEP 325 switch expressions and* switch statements.*/
public class GrowingEnumSwitchDemo
{public static void printResponseStringFromStatement(final Response response){out.println("Statement [" + response.name() + "]:");switch (response){case YES:out.println("Si!");break;case NO:out.println("No!");break;}}public static void printResponseStringFromExpression(final Response response){out.println("Expression [" + response.name() + "]:");out.println(switch (response){case YES -> "Si!";case NO -> "No!";});}public static void main(final String[] arguments){if (arguments.length < 1){out.println("Provide an appropriate 'dustin.examples.jdk12.switchexp.Response' string as an argument.");System.exit(-1);}final String responseString = arguments[0];out.println("Processing string '" + responseString + "'.");final Response response = Response.valueOf(responseString);printResponseStringFromStatement(response);printResponseStringFromExpression(response);}
}
上面的代码( 在GitHub上也可用 )将编译而不会发生意外,并且当我在GrowingEnumSwitchDemo
类上执行main
函数并将其传递给“ YES”字符串时,它将按预期工作。 如果我向Response
枚举添加一个新值MAYBE
并仅编译该枚举Java文件 ,然后使用字符串“ MAYBE”运行GrowingEnumSwitchDemo.main(String[])
, GrowingEnumSwitchDemo.main(String[])
遇到IncompatibleClassChangeError 。 接下来显示新的Response.java
清单,其后是一个屏幕快照,该屏幕快照演示了仅用新值重新编译枚举并使用先前编译的调用代码运行后刚刚描述的问题。
package dustin.examples.jdk12.switchexp;/*** Enum representation of a response.*/
public enum Response
{YES,NO,MAYBE;
}
IncompatibleClassChangeError的存在使我们很明显地发现,枚举上存在一个以前未由switch
表达式处理的新值。 这使开发人员可以通过为枚举值添加case
或通过添加全部default
值来修复switch
表达式。 这可能会比今天的当前情况更好,在当前情况下,使用:
/ break
语法的switch
语句将在相同情况下无提示地继续运行(在先前的代码清单和屏幕快照中也得到了证明)。
关于通过JEP 325引入Java的增强功能,有几件令人喜欢的事情。“箭头”语法使switch
表达式和switch
语句不必承受令人惊讶的范围问题,无意跌倒的风险或需要明确的break
s的负担。 此外,必须返回值的switch
表达式可以与枚举结合使用,以确保所有枚举值始终在编译时进行处理(如果不是在编译时处理所有枚举值,则不会进行编译)如果所使用的枚举具有添加的值并与先前编译的客户端代码一起使用,则会引发错误。
翻译自: https://www.javacodegeeks.com/2018/09/jdk-12-switch-expression-encountering-unanticipated-enum-value.html