微言码道 · 手记

Java 8之后的那些新特性(七):switch表达式 Switch Expressions

—— Java程序员应该非常熟悉switch语句,因为它有个break不能忽略的问题被反复强制。 不过,是时候忘掉它了,现在Java有了更好的switch表达式了

对于Java程序员来说,switch语句应该是非常熟悉的了,它是Java中结构控制的一种。相信大家使用的都比较多了。

但其实在Java 12,13以及最终的17中,Java都引进与优化了与switch语句有关联的一个新特性,那就是switch表达式

这篇文章是Java 8之后的那些新特性系列的第七篇。本系列的其它文章为:

  1. Java 8之后的那些新特性(一):局部变量var
  2. Java 8之后的那些新特性(二):文本块 Text Blocks
  3. Java 8之后的那些新特性(三):Java System Logger
  4. Java 8之后的那些新特性(四):网络请求 Java Http Client
  5. Java 8之后的那些新特性(五): Helpful NullPointerExceptions
  6. Java 8之后的那些新特性(六):记录类 Record Class

从switch语句说起

要注意区分下,Switch Statement(switch语句)与Switch Expressions(switch表达式)两个词的不同。switch语句是大家熟悉的旧的switch用法,而 switch表达式则是我们今天要说的。

其实,从这两者之间的名字来看,就能猜到两者肯定有很大的相似性,也一定会有不同。

所以,要说switch表达式,我们一定得从switch语句说起。

public class SwitchTest {

    @Test
    void testSwitchStatement(){
        var trafficLight = randomTrafficLight();
        String lightString;
        switch (trafficLight){
            case LIGHT_RED: {
                lightString = "红灯";
                break;
            }
            case LIGHT_GREEN:{
                lightString = "绿灯";
                break;
            }
            case LIGHT_YELLOW: {
                lightString = "黄灯";
                break;
            }
            default: throw new IllegalArgumentException("不支持的信号灯");
        }
        Assertions.assertNotNull(lightString);
    }
}

上述代码应该是大家都非常熟悉的switch语句的代码。在讨论过往的switch语句时,无论大家看哪个教程,都一定会提醒大家一件事:

不要忘记break语句

否则switch语句的执行过程会出乎你的意料之外。

当然,随着Java的发展,switch语句出现了一些问题,有待改进。主要包括:

  • 上面提及的break语句这个点,这个在设计上就非常糟糕
  • switch语句不是表达式,通过switch语句覆值无法做到
  • 对Java中最新的Pattern Matching无法支持

因此,这也是switch表达式出现的原因所在。switch表达式其实你可以认为它就是switch语句的改进版。它主要解决了switch语句在上述三个方面的问题。

switch表达式

好吧,我们用switch表达式来重写上面这个代码,你看到的效果将是这样

public class SwitchTest {

    @Test
    void testSwitchExpression(){
        var trafficLight = randomTrafficLight();
        var lightString = switch (trafficLight) {
            case LIGHT_YELLOW -> {
                yield  "黄灯";
            }
            case LIGHT_GREEN -> {
                yield  "红灯";
            }
            case LIGHT_RED -> {
                yield  "红灯";
            }
        };
        Assertions.assertNotNull(lightString);
    }
}

这是使用switch表达式来改写后的代码(这个可以再简化,但为了足够说明yield的作用,先这样)

通过上面这个改写,你可以很明显感知到,switch表达式可以做到:

  • switch表达式是一个表达式,它有值返回的
  • switch表达式并不需要break,这个点被改进了

而在switch表达式中,返回值是通过yield字段来处理的。并且,如果你只有一行返回值的情况下,可以省略掉block括号以及yield字段。

public class SwitchTest {
    @Test
    void testSwitchExpression(){
        var trafficLight = randomTrafficLight();
        var lightString = switch (trafficLight) {
            case LIGHT_YELLOW -> "黄灯";
            case LIGHT_GREEN -> "绿灯";
            case LIGHT_RED -> "红灯";
        };
        Assertions.assertNotNull(lightString);
    }
}

switch表达式的注意点

语法规则

注意,switch表达式的标准语法是

case label_1, label_2, ..., label_n -> expression;|throw-statement;|block 

switch表达式用的是 ->(箭头号)*,而在switch语句中,用的是**: (冒号)

switch表达式必须穷尽一切可能

与switch语句不同,switch语句并不需要穷尽一切值,而switch表达式则一定要穷尽一切可能值,如果你不想穷尽所有值,可以使用default.

public class SwitchTest {
    @Test
    void testSwitchStatement(){
        var trafficLight = randomTrafficLight();
        String lightString = null;
        //switch语句可以不列出所有值,也可以没有default
        switch (trafficLight){
            case LIGHT_RED: {
                lightString = "红灯";
                break;
            }
            case LIGHT_GREEN:{
                lightString = "绿灯";
                break;
            }
        }
        Assertions.assertNotNull(lightString);
    }
}

但在switch表达式中,这是不允许的

public class SwitchTest {
    @Test
    void testSwitchExpression(){
        var trafficLight = randomTrafficLight();
        //这是不允许的,LIGHT_RED没有列上
        var lightString = switch (trafficLight) {
            case LIGHT_YELLOW -> "黄灯";
            case LIGHT_GREEN -> "绿灯";
        };
        Assertions.assertNotNull(lightString);
    }
}

上述代码会抛出编译错误:‘switch’ expression does not cover all possible input values

你可以加上case LIGHT_RED -> "红灯";或者使用default,这两种方式都可以。

switch表达式与Pattern Matching

上述只讲了switch表达式的两点,一个是它是表达式,二它不需要break,而switch表达式还可以搭配patten matching一同使用,做到模式自动匹配

这一点,我在下次再来说,聊一聊Pattern Matching这个新特性。

好了,这就是关于switch表达式的新特性,有了它,你可以忘掉过去那个会经常出错的switch语句了。