Java 13: Switch表达式

以下代码需要使用Java 13 才能运行,并且需要设置语言级别为13(Preview).IDEA中File-Project Structure-Project language level-选择 13(Preview)-Switch expressions,text blocks.

以下内容机翻整理自OpenJDK官方:JEP 354,稍有改动.

简述

在Java 13发布中,提供了两个预览功能:一个是文本块,另一个是Switch表达式改进.这篇是关于Switch表达式改进的介绍.文本块的文章地址是: Java 13预览:文本块

基本操作

在最开始我们是这样用Switch表达式的:

        int day = SUNDAY;
        switch (day) {
            case MONDAY:
            case FRIDAY:
            case SUNDAY:
                System.out.println(6);
                break;
            case TUESDAY:
                System.out.println(7);
                break;
            case THURSDAY:
            case SATURDAY:
                System.out.println(8);
                break;
            case WEDNESDAY:
                System.out.println(9);
                break;
        }

通过break的”作用”,合并计算多个结果.看起来似乎没什么问题,但如果有个break写错了,也就意味着计算结果出现了偏差.

在Java 13提供的Switch表达式,是这样的:

        switch (day) {
            case MONDAY, FRIDAY, SUNDAY -> {
                System.out.println(5);
            }
            case TUESDAY -> System.out.println(7);
            case THURSDAY, SATURDAY -> System.out.println(8);
            case WEDNESDAY -> System.out.println(9);
        }

通过一种优雅的方式,重写了上面的示例代码.最主要的好处是,去掉了break,减少错误的发生.

case MONDAY, FRIDAY, SUNDAY -> {
                System.out.println(5);
}

这个地方是为了演示,在Switch的case中,也可以使用代码块.

使用Switch动态取值

有时候我们会这样写代码,反正我是这样写过…

通过Switch来动态获取值.

在以前我们是这样写的:

        int numLetters;
        switch (day) {
            case MONDAY:
            case FRIDAY:
            case SUNDAY:
                numLetters = 6;
                break;
            case TUESDAY:
                numLetters = 7;
                break;
            case THURSDAY:
            case SATURDAY:
                numLetters = 8;
                break;
            case WEDNESDAY:
                numLetters = 9;
                break;
            default:
                throw new IllegalStateException("Wat: " + day);
        }
        System.out.println(numLetters);

通过break和case结合起来,对Switch外面的变量进行动态赋值,看起来略微复杂了一些.现在,Java 13又提供了一个简单的办法:

        int day1 = MONDAY;
        String n1umLetters = switch (day1) {
            case MONDAY, FRIDAY, SUNDAY -> "6";
            case TUESDAY -> "7";
            case THURSDAY, SATURDAY -> "8";
            case WEDNESDAY -> "9";
            // 这样使用,必须添加default,否则会提示: Switch expression does not cover all possible input value(开关表达式未涵盖所有可能的输入值)
            // 意思就是,如果输入的值没有在case中,则无法找到返回值.
            default -> "default";
        };

在这个地方用String是为了演示String在其中同样可用.

另外需要注意的一点是:

这样使用,必须添加default,否则会提示: Switch expression does not cover all possible input value(开关表达式未涵盖所有可能的输入值).意思就是,如果输入的值没有在case中,则无法找到返回值.

java标志
image-2833

关于箭头标签

如果case匹配,则执行箭头右侧的表达式或语句,否则不执行任何操作

    static void howMany(int k) {
        switch (k) {
            case 1 -> System.out.println("one");
            case 2 -> System.out.println("two");
            default -> System.out.println("many");
        }
    }

看起来简洁明了.同时也可以在下面的示例中使用上面的方式定义代码:

    static void howMany1(int k) {
        System.out.println(
                switch (k) {
                    case 1 -> "one";
                    case 2 -> "two";
                    default -> "many";
                }
        );
    }

在代码块中使用yield

在Switch表达式返回的值中,有可能会使用到代码块.而这时,我们则可以用到yield语句.如下所示:

int j = switch (day) {
            case MONDAY -> 0;
            case TUESDAY -> 1;
            default -> {
                int k=day;
                int result=fyield(k);
                yield result;
            }
};

static int fyield(int i) {
        return i * 2;
}

这样就可以调用其它方法,来进行返回值的处理之后,再返回.

来自官方文档的可能遇到的错误提示

机翻.

Switch表达的情况必须详尽 ; 对于所有可能的值,必须有一个匹配的开关标签。(显然,Switch声明并非必须详尽。)

实际上,这通常意味着需要一个default子句。但是,enum Switch对于覆盖所有已知常量的表达式,default编译器会插入一个子句以指示该enum定义在编译时和运行时之间已更改。依靠这种隐式default子句的插入可以使代码更健壮。现在,当重新编​​译代码时,编译器将检查所有情况是否得到明确处理。如果开发人员插入了显式default子句(如今天的情况),则可能的错误将被隐藏。

此外,Switch表达式必须以一个值正常完成,或者必须通过引发异常来突然完成。这有许多后果。首先,编译器会检查每个开关标签是否匹配,然后产生一个值。

int i = switch (day) {
    case MONDAY -> {
        System.out.println("Monday"); 
        // ERROR! Block doesn't contain a yield statement
    }
    default -> 1;
};
i = switch (day) {
    case MONDAY, TUESDAY, WEDNESDAY: 
        yield 0;
    default: 
        System.out.println("Second half of the week");
        // ERROR! Group doesn't contain a yield statement
};

另一种后果是,控制语句,break,yield,return和continue,无法通过跳出Switch表达式,如在下文中:

 for (int i = 0; i < MAX_VALUE; ++i) {
        int k = switch (e) { 
            case 0:  
                yield 1;
            case 1:
                yield 2;
            default: 
                continue z; 
                // ERROR! Illegal jump through a Switch expression 
        };
    ...
    }

示例源码

示例完整源码如下:

import static java.util.Calendar.*;


public class Java13SwitchTestDemo {


    public static void main(String[] args) {
        // 简述
        int day = SUNDAY;
        switch (day) {
            case MONDAY:
            case FRIDAY:
            case SUNDAY:
                System.out.println(6);
                break;
            case TUESDAY:
                System.out.println(7);
                break;
            case THURSDAY:
            case SATURDAY:
                System.out.println(8);
                break;
            case WEDNESDAY:
                System.out.println(9);
                break;
        }


        // Java 13 新的方式
        switch (day) {
            case MONDAY, FRIDAY, SUNDAY -> {
                System.out.println(5);
            }
            case TUESDAY -> System.out.println(7);
            case THURSDAY, SATURDAY -> System.out.println(8);
            case WEDNESDAY -> System.out.println(9);
        }

        int numLetters;
        switch (day) {
            case MONDAY:
            case FRIDAY:
            case SUNDAY:
                numLetters = 6;
                break;
            case TUESDAY:
                numLetters = 7;
                break;
            case THURSDAY:
            case SATURDAY:
                numLetters = 8;
                break;
            case WEDNESDAY:
                numLetters = 9;
                break;
            default:
                throw new IllegalStateException("Wat: " + day);
        }
        System.out.println(numLetters);


        int day1 = MONDAY;
        String n1umLetters = switch (day1) {
            case MONDAY, FRIDAY, SUNDAY -> "6";
            case TUESDAY -> "7";
            case THURSDAY, SATURDAY -> "8";
            case WEDNESDAY -> "9";
            default -> "default";
        };

        howMany(1);
        howMany(2);
        howMany(3);

        System.out.println("------");
        howMany1(8);
        howMany1(9);
        howMany1(10);
        System.out.println("------");

        int j = switch (day) {
            case MONDAY -> 0;
            case TUESDAY -> 1;
            default -> {
                int k=day;
                int result=fyield(k);
                yield result;
            }
        };
        System.out.println(j);

    }

    static int fyield(int i) {
        return i * 2;
    }

    /**
     * 箭头标签
     * <p>
     * 如果case匹配,则执行箭头右侧的表达式或语句,否则不执行任何操作,
     *
     * @param k
     */
    static void howMany(int k) {
        switch (k) {
            case 1 -> System.out.println("one");
            case 2 -> System.out.println("two");
            default -> System.out.println("many");
        }
    }


    /**
     * 可以重写上面的方法为
     *
     * @param k
     */
    static void howMany1(int k) {
        System.out.println(
                switch (k) {
                    case 1 -> "one";
                    case 2 -> "two";
                    default -> "many";
                }
        );
    }

}