java switch二三事

switch二三事.png

概述

switch是在java开发中一个很常用的关键字,一般用于对于一个变量等于不同值时候做不同的处理,比起直接用if、else if、else if、else这样会显得简洁一些,也更加符合人最直观的理解,加强代码的可读性。

传统用法

jdk7之前,switch只有1种用法,switch仅支持整形和枚举。实际上,枚举也是通过ordinal()方法转换成了int。所以,可以简单理解成仅支持整形。

穿透

相比于直接用if、else if、else if、else这样的写法,switch有个比较大的特点是穿透,即一旦符合条件进入了case,如果没有显示break,就会一直进入下面的case,例如我们要写个函数求月份归属的季节,代码如下:

private static Season getSeasonByMonth(int month) {
    Season season = null;
    // 这里简单认为春季是2、3、4月,夏季是5、6、7月,秋季是8、9、10月,冬季是11、12、1月
    // 注意这里month的取值在0-11之间,是日常理解的月份-1
    switch (month) {
        case 1:
        case 2:
        case 3:
            season = Season.SPRING;
            break;
        case 4:
        case 5:
        case 6:
            season = Season.SUMMER;
            break;
        case 7:
        case 8:
        case 9:
            season = Season.AUTUMN;
            break;
        case 10:
        case 11:
        case 0:
            season = Season.WINTER;
            break;
        default:
            System.out.println("month值不合法,未能找到归属的季节");
    }
    return season;
}

这里就利用了穿透的特性,避免了代码重复。不过这种特性也带来了额外的成本,就是有点反直觉,天然认为不符合case条件就不会进入,忘记写break而导致代码bug是初级程序员使用switch常见的错误。
另外,使用switch时候,最后写上default是一个好习惯,作用跟else是类似的,可以确保这个switch语句逻辑完整,无论什么值,都会走到一种情况,即使在写的时候,觉得这个default永远也不会进去,但是程序异常往往会发生在这些意想不到的地方。

匹配String

从jdk1.7开始,switch支持String,不过这里其实只是新增的一个语法糖,switch String实际上是利用了String自身的hashCode()方法转换成了int,然后通过equles来保护哈希冲突的情况,实际上JVM层面依然还是只支持整形,跟switch 枚举有些类似。

jdk14的新用法

需要说明,以下的一些特性在jdk12、jdk13就已经开始预览,归于jdk14是因为在jdk14中才成为正式的特性。

yield返回值

switch语句之前仅仅是在某些情况下替代if,这里可以使用yield来设置返回值,switch就正式变成了表达式,上面的方法可以写成:

Season season = switch (month) {
    case 1:
    case 2:
    case 3:
        yield Season.SPRING;
    case 4:
    case 5:
    case 6:
        yield Season.SUMMER;
    case 7:
    case 8:
    case 9:
        yield Season.AUTUMN;
    case 10:
    case 11:
    case 0:
        yield Season.WINTER;
    default:
        System.out.println("month值不合法,未能找到归属的季节");
        yield Season.SPRING;
};

case多值 和 箭头表达值

之前,我们想在switch等于某些值的时候执行相同的逻辑,我们会使用switch穿透的特性,但是这个特性是双刃剑,也比较容易出错,这里支持case多值之后,就有了更加简洁直观的写法,符合人的直觉,例如下面的方法可以改写成:

private Season getSeasonByMonth(int month) {
    Season season = null;
    // 这里简单认为春季是2、3、4月,夏季是5、6、7月,秋季是8、9、10月,冬季是11、12、1月
    // 注意这里month的取值在0-11之间,是日常理解的月份-1
    switch (month) {
        case 1, 2, 3 -> season = Season.SPRING;
        case 4, 5, 6 -> season = Season.SUMMER;
        case 7, 8, 9 -> season = Season.AUTUMN;
        case 10, 11, 0 -> season = Season.WINTER;
        default -> System.out.println("month值不合法,未能找到归属的季节");
    }
    return season;
}

jdk17的新用法

模式匹配

这里的模式匹配是指,可以通过传入对象的不同类型,来走不同的逻辑,而不是仅仅是不同的值,例如:

String s1 = switch (o) {
    case Integer ii -> "o is Integer";
    case String ss -> "o is String, length = " + s.length();
    default -> "unknown";
};

注意,这里匹配了类型之后,可以用一个变量接收,这样可以直接使用类型自带的方法了,不然还需要对o进行一次强转,这样会更加方便。

保护模式

case条件不仅仅是一个值,还可以增加表达式进行综合判断,前面说过的支持switch String的语法糖就适合这种保护模式,因为还需要equles来进行一次保护校验

String s1 = switch (o) {
    case Integer ignored -> "o is Integer";
    case String ignored && "123".equals(o) -> "o is String";
    default -> "unknown";
};

空值

之前switch是无法接受空值的,传空就会抛出空指针异常,这里支持空值后,不需要在switch之前判空,空也可以作为一种值,跟其他的值一起并列:

switch (s) {
    case "123":
        System.out.println("s is 123");
        break;
    case null:
        System.out.println("s is null");
        break;
}

总结

switch在java开发中是个历史悠久的关键字,也比较常用。传统用法时,注意穿透现象,以及写上default让逻辑完备。在更新版本的jdk中,可以使用case多值来代替之前的穿透,用yield或者箭头来做值返回。特定情况下模式匹配、保护模式、空值等新用法也会让代码更加简洁和易于理解。
说句题外话,既然新版本这么香,就不要一直守着java8不放了对不对,毕竟,不断尝试新的东西,也是一个程序猿基本的素养。

全部评论

相关推荐

点赞 收藏 评论
分享
牛客网
牛客企业服务