c语言中switch表达式
Java 14 is going to be released on March 17, 2020. The new version of Java contains an interesting update to the Java language: new switch
expressions. Let’s see how the new switch
expressions can be used, what kind of advantages they offer, and what can potentially go wrong. In the end, we’ll tackle a tricky question about switch
expressions.
Java 14将于2020年3月17日发布。Java的新版本包含对Java语言的有趣更新:新的switch
表达式。 让我们看看如何使用新的switch
表达式,它们提供什么样的优势以及潜在的错误。 最后,我们将解决一个关于switch
表达式的棘手问题。
经典的“转换”声明 (The Classic ‘Switch’ Statement)
The current design of the switch
statement in Java follows languages such as C and C++. It works only as a statement and supports fall-through semantics by default. Here’s an example of the classic switch
statement with an enum:
Java中switch
语句的当前设计遵循诸如C和C ++之类的语言。 它仅用作语句,并且默认情况下支持穿透语义。 这是带有枚举的经典switch
语句的示例:
public class ClassicSwitchStatement {
enum Person {
Mozart, Picasso, Goethe, Dostoevsky, Prokofiev, Dali
}
public static void main(String[] args) {
print(Person.Mozart);
print(Person.Dali);
print(Person.Dostoevsky);
}
static void print(Person person) {
switch (person) {
case Dali:
case Picasso:
System.out.printf("%s was a painter%n", person);
break;
case Mozart:
case Prokofiev:
System.out.printf("%s was a composer%n", person);
break;
case Goethe:
case Dostoevsky:
System.out.printf("%s was a writer%n", person);
break;
default:
throw new IllegalArgumentException(
String.format("Unknown person: %s", person));
}
}
}
You might have noticed many case and break statements in the example above. Those statements introduce some visual noise and make the code unnecessarily verbose. This visual noise may then mask mistakes such a missing break statement, which would mean accidental fall through.
在上面的示例中,您可能已经注意到很多case和break语句。 这些语句引入了一些视觉干扰,并使代码不必要地冗长。 然后,这种视觉噪音可能掩盖了诸如丢失中断声明之类的错误,这将意味着意外掉线。
新的“ Switch”表达 (The New ‘Switch’ Expressions)
Java 14 extends switch
so it can be used as either a statement or an expression. In particular, the new Java introduces the following:
Java 14扩展了switch
因此它可以用作语句或表达式。 特别是,新的Java引入了以下内容:
A new form of the
switch
labelcase ... ->
, where only the code to the right of the label is going to be executed if the label is matched. The code to the right of acase ... ->
label may be an expression, a block, or a throw statement.switch
标签case ... ->
一种新形式case ... ->
,如果匹配标签,则只执行标签右边的代码。case ... ->
标签右侧的代码可以是表达式,块或throw语句。A new
yield
statement to yield a value that becomes the value of the enclosingswitch
expression一个新的
yield
语句,用于产生一个值,该值成为封闭switch
表达式的值- Multiple constants per case that are separated by commas 每个个案的多个常数,以逗号分隔
With the new Java 14, it’s possible to use both traditional case ... :
labels and the new case ... ->
labels. It’s important the traditional labels still support fall through by default, but the new ones don't need to.
使用新的Java 14,可以同时使用传统case ... :
标签和新case ... ->
标签。 重要的是,默认情况下,传统标签仍然支持掉线,但新标签则不需要。
Let’s see how the example above can be rewritten with the new switch
expressions:
让我们看看如何使用新的switch
表达式重写上面的示例:
public class WhoIsWho {
enum Person {
Mozart, Picasso, Goethe, Dostoevsky, Prokofiev, Dali
}
public static void main(String[] args) {
print(Person.Mozart);
print(Person.Dali);
print(Person.Dostoevsky);
}
static void print(Person person) {
String title = switch (person) {
case Dali, Picasso -> "painter";
case Mozart, Prokofiev -> "composer";
case Goethe, Dostoevsky -> "writer";
};
System.out.printf("%s was a %s%n", person, title);
}
}
This simple example can be compiled and run with just a single command (thanks to JEP 330, which has allowed the launching of single-file source-code programs since Java 11):
这个简单的示例仅需一个命令即可编译和运行(由于JEP 330,自Java 11开始就允许启动单文件源代码程序):
$ java WhoIsWho.java
Mozart was a composer
Dali was a painter
Dostoevsky was a writer
You can see several case statements have been merged, and the code doesn't use a break statement any more. As a result, the print()
method became much shorter and looks nicer.
您会看到几个case语句已经合并,并且代码不再使用break语句。 结果, print()
方法变得更短并且看起来更好。
The following example shows a multiline default
block that uses the new yield
statement to yield a value. Note that there are several new constants in the Person
enum that aren’t covered by the case
labels in the switch
expression:
以下示例显示了一个多行default
块,该块使用新的yield
语句产生一个值。 请注意, Person
枚举中有几个新常量未包含在switch
表达式中的case
标签中:
public class WhoIsWho {
enum Person {
Mozart, Picasso, Goethe, Dostoevsky, Prokofiev, Dali,
Gaudi, Bach, Einstein
}
public static void main(String[] args) {
print(Person.Mozart);
print(Person.Dali);
print(Person.Einstein);
}
static void print(Person person) {
String title = switch (person) {
case Dali, Picasso -> "painter";
case Mozart, Prokofiev -> "composer";
case Goethe, Dostoevsky -> "writer";
default -> {
System.out.printf("Oops! I don't know about %s%n", person);
yield "...";
}
};
System.out.printf("%s was a %s%n", person, title);
}
}
Here’s what the program prints out:
程序输出如下:
$ java WhoIsWho.java
Mozart was a composer
Dali was a painter
Oops! I don't know about Einstein
Einstein was a ...
The next example shows how factorial
may be implemented with the new switch
expressions:
下一个示例显示如何使用新的switch
表达式实现factorial
:
static int factorial(int n) {
return switch (n) {
case 0, 1 -> 1;
case 2 -> 2;
default -> factorial(n - 1) * n;
};
}
重要细节 (Important Details)
There are several important things to know about the new switch
expressions.
关于新的switch
表达式,有几件重要的事情要知道。
The first thing to know is the cases of a switch
expression must be exhaustive. In other words, for all possible values, there must be a matching switch
label. Let’s just add a new element to the enum and see what’s going to happen:
首先要知道的是switch
表达式的情况必须详尽。 换句话说,对于所有可能的值,必须有一个匹配的switch
标签。 让我们向枚举添加一个新元素,看看会发生什么:
public class InvalidWhoIsWho {
enum Person {
Mozart, Picasso, Goethe, Dostoevsky, Prokofiev, Dali,
// this element is not covered
// by any case in the switch expression below
// which results to compilation failure
Gaudi
}
public static void main(String[] args) {
print(Person.Mozart);
print(Person.Dali);
print(Person.Dostoevsky);
}
static void print(Person person) {
String title = switch (person) {
case Dali, Picasso -> "painter";
case Mozart, Prokofiev -> "composer";
case Goethe, Dostoevsky -> "writer";
};
System.out.printf("%s was a %s%n", person, title);
}
}
Compilation will fail right away with the following error message:
编译将立即失败,并显示以下错误消息:
$ java InvalidWhoIsWho.java
InvalidWhoIsWho.java:19: error: the switch expression does not cover all possible input values
String title = switch (person) {
^
1 error
error: compilation failed
Adding a simple default
case makes the Java compiler happy:
添加一个简单的default
情况使Java编译器满意:
String title = switch (person) {
case Dali, Picasso -> "painter";
case Mozart, Prokofiev -> "composer";
case Goethe, Dostoevsky -> "writer";
default -> "...";
};
In general, unless an enum is used and the cases of a switch
expression cover all constants, a default
clause is required in the switch
expression.
通常,除非使用一个枚举和的情况下, switch
表达涵盖所有常量,一个default
子句需要在switch
的表达。
The second thing to remember is a switch
expression must either complete normally with a value or by throwing an exception. Let’s take a look at the following code:
要记住的第二件事是, switch
表达式必须正常完成一个值或抛出异常。 让我们看下面的代码:
public class InvalidSwitchExpressionWithoutDefault {
public static void main(String[] args) {
System.out.println(print(1));
}
static String print(int n) {
return switch (n) {
case 0 -> "zero";
case 1 -> "one";
case 2 -> "two";
};
}
}
If we try to compile this code, the Java compiler will immediately complain:
如果我们尝试编译此代码,则Java编译器将立即抱怨:
InvalidSwitchExpressionWithoutDefault.java:8: error: the switch expression does not cover all possible input values
return switch (n) {
^
1 error
error: compilation failed
Again, adding a default
case makes it work:
同样,添加default
大小写使其起作用:
static String print(int n) {
return switch (n) {
case 0 -> "zero";
case 1 -> "one";
case 2 -> "two";
default -> "many";
};
}
The third important thing to keep in mind is yield
is now a restricted identifier. In particular, it means that classes named yield
become illegal:
要记住的第三件事是, yield
现在是一个受限制的标识符。 特别是,这意味着名为yield
类变为非法:
$ cat YieldClassName.java
class yield {}
$ javac YieldClassName.java
YieldClassName.java:1: error: 'yield' not allowed here
class yield {
^
as of release 13, 'yield' is a restricted type name and cannot be used for type declarations
1 error
error: compilation failed
However, it’s allowed to use yield
as a variable or a method name:
但是,允许将yield
用作变量或方法名称:
$ cat ValidUseOfYieldWord.java
public class ValidUseOfYieldWord {
void yield() {
int yield = 0;
}
}
$ javac ValidUseOfYieldWord.java && echo ok || echo failed
ok
结论 (Conclusion)
Here’s what the authors say about the new switch
expressions:
这是作者对新switch
表达式的评价:
“These changes will simplify everyday coding.”
“这些更改将简化日常编码。”
Let’s see.
让我们来看看。
奖金 (Bonus)
What do you think is going to happen? Here are the options:
您认为会发生什么? 以下是选项:
- Compilation error. 编译错误。
- Runtime error. 运行时错误。
Oops
is printed out.Oops
!OopsOops
is printed outOopsOops
打印出来Forty-two
is printed out.打印出
Forty-two
。
public class StrangeYield {
public static void main(String[] args) {
go(0);
}
static void go(int n) {
System.out.println(
switch (n) {
case 42 -> "Fotry-two";
default -> {
yield("Oops");
}
}
);
}
static String yield(String s) {
return s + "Oops";
}
}
翻译自: https://medium.com/better-programming/a-look-at-the-new-switch-expressions-in-java-14-ed209c802ba0
c语言中switch表达式