java switch二三事

概述

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

传统用法

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

穿透

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

csharp 复制代码
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就正式变成了表达式,上面的方法可以写成:

csharp 复制代码
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多值之后,就有了更加简洁直观的写法,符合人的直觉,例如下面的方法可以改写成:

ini 复制代码
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的新用法

模式匹配

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

arduino 复制代码
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来进行一次保护校验

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

空值

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

csharp 复制代码
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不放了对不对,毕竟,不断尝试新的东西,也是一个程序猿基本的素养。

相关推荐
小信丶12 小时前
Spring Cloud Stream EnableBinding注解详解:定义、应用场景与示例代码
java·spring boot·后端·spring
无限进步_12 小时前
【C++】验证回文字符串:高效算法详解与优化
java·开发语言·c++·git·算法·github·visual studio
亚历克斯神12 小时前
Spring Cloud 2026 架构演进
java·spring·微服务
七夜zippoe12 小时前
Spring Cloud与Dubbo架构哲学对决
java·spring cloud·架构·dubbo·配置中心
海派程序猿12 小时前
Spring Cloud Config拉取配置过慢导致服务启动延迟的优化技巧
java
阿维的博客日记13 小时前
为什么不逃逸代表不需要锁,JIT会直接删掉锁
java
William Dawson13 小时前
CAS的底层实现
java
ffqws_13 小时前
Spring Boot入门:通过简单的注册功能串联Controller,Service,Mapper。(含有数据库建立,连接,及一些关键注解的讲解)
数据库·spring boot·后端
程序边界13 小时前
行标识符机制的技术演进与实践(下)——ROWID与实战应用
后端
九英里路13 小时前
cpp容器——string模拟实现
java·前端·数据结构·c++·算法·容器·字符串