尝鲜Java 12新特性:switch表达式
Java 12将在两个月后(2019/3/19)发布,现已进入RDP1阶段,确定加入8个JEP。其中对Java语法的改进是JEP 325: switch表达式。于是我迫不及待,提前感受一下更先进的语言特性。
因为12没有正式发布,本文使用自己编译的OpenJDK。嫌麻烦的话,也可以直接使用官方的ea版本。JEP325是预览(preview)特性,编译运行时需要添加--enable-preview参数。
顾名思义,这个feature是对switch动手脚的。包括两个方面。
1. 简化fall-through规则
下面这样的switch代码我们写过几万遍了
switch (today) {
case SATURDAY:
case SUNDAY:
System.out.println("I'm happy!");
break;
case MONDAY:
case TUESDAY:
case WEDNESDAY:
case THURSDAY:
case FRIDAY:
System.out.println("I'm sad...");
break;
default:
System.out.println("I'm confused.");
}
这段代码存在的问题是:
1. 内容不符合爱岗敬业的核心价值观(敲黑板!重要!!)
2. 多个条件对应相同代码时(比如MONDAY到FRIDAY),要重复写多个case,冗余且丑陋
3. 每一段代码后面都要有break,一旦忘记就会有编译器检测不到的逻辑错误
4. 变量作用域混乱
第四个问题可能长被忽略。case或者default后面是一连串的语句,而不是代码块(注意,它是没有大括号的)。这种情况下定义的局部变量,其作用域不是case后的部分,而是整个switch结构。因此,下面的代码无法通过编译。
switch (today) {
case MODAY:
int x = 1;
break;
default:
int x = 0; //Variable x is already defined in the scope
}
编译器看到的是在一个作用域中存在两个x,非常违背人类的直觉。
上面的四个问题,除了1,剩下的万恶之源就是fall-through规则。即switch结构在找到第一个匹配的case条件后,会顺序执行后面所有case对应的代码,无论是否判断为真。这是40多年前C语言创造后来Java原样照抄的经典语法,但在今天看起来就显得很呆萌了,新的语言也几乎都放弃了fall-through。
好在,尽管后知后觉,从12开始Java开发者也可以选择更简洁清晰的语法了。就像这样
switch (today) {
case SUNDAY, SATURDAY -> System.out.println("I'm happy!");
case MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY -> System.out.println("I'm happy, too!!");
default -> System.out.println("I'm confused.");
}
很容易看出语法的变化,这些变化也解决了上面的四个问题。归纳一下:
1. 程序内容积极向上,体现了新时代的奋斗精神(敲黑板!重要!!)
2. 对应相同动作的多个case合并为一行,代码更简洁
3. 条件和动作之间用->连接,这时fall-through规则失效。匹配到的分支代码执行完后直接跳出,不会继续执行下面的case对应的代码。也就是不需要再为每一个分支写break了。程序更简洁清晰,也更符合人类的直觉。
需要注意,为了保持向后兼容性,case条件后依然可以使用:,这时fall-through还是有效的,即不能省略原有的break。而一个switch结构里不能混用->和:,否则会有编译错误。
4. 每一个->后面只允许接一个表达式、一个代码块、或者一个throw语句。这样在代码块中定义的局部变量,其作用域就限制在代码块中,而不是蔓延到整个switch结构。逻辑更加清楚了。
2. switch作为表达式(expression)
switch结构一直是一个statement,而从Java 12开始,它也可以用作expression。从学院派的定义理解statement和expression的区别叫人头疼,如果说人话的话,就是switch可以有返回值了。
作为statement的switch没有返回值,所以我们不能写出这样的代码
x = switch (y) { ... }
如果需要根据不同的条件给某个变量赋值,我们以前只能这样做
String word = "";
switch (num) {
case 1:
word = "One";
break;
case 2:
word = "Two";
break;
default:
String result = String.format("Other (%d)", num);
word = result;
}
让人难受的地方有两个。
1. 重复多次地写赋值语句,繁琐且易错。
2. 这段程序的终极目标是为word变量赋值,而赋值前必须在其他的地方初始化word,淡化了二者的逻辑关系,代码也显得琐碎。
从12开始我们可以这样改造代码
String word = switch (num) {
case 1 -> "One";
case 2 -> "Two";
default -> {
String result = String.format("Other (%d)", num);
break result;
}
};
可见,switch成了一个表达式(expression),它有自己的返回值。每一个分支只需要决定具体的返回值是什么,不需要考虑如何使用这个值。而全程只需要一次赋值操作。代码整体变得更简洁、紧凑、清晰。
而返回值又有两种写法。还记得吗,上一节提到过,->后只能接三样东西:表达式、代码块、throw语句。throw的情况没有返回值,先不管它。另外两种情况:
1. 如果分支只有一个表达式,那么表达式本身就是switch的值,比如上面例子里的"One"和"Two";
2. 如果分支是一个代码块,比如例子中的default,可以看到Java 12改造了break关键字,可以通过break result的形式返回值。switch并没有抛弃break,而是赋予它更重要的职能。
作为expression的switch也可以使用:,在这种情况下,各个分支必须用break关键字返回值。像这样
String word = switch (num) {
case 1 : break "One";
case 2 : break "Two";
default : {
String result = String.format("Other (%d)", num);
break result;
}
};
上面例子中,case 1和case 2中的break不能省略,否则会有编译错误。
很显然,当switch用作expression时,每一个分支都必须有返回值(或者有throw异常)。我们不能写下面这样的代码
String word = switch (num) {
case 1 -> "One";
case 2 -> "Two";
default -> {
System.out.println("莫挨老子");
//错误: switch rule completes without providing a value
}
};
编译器不知道当num=3的时候应该返回什么,于是它愤怒地抛出了一个错误。
最后要强调,switch在不返回值的时候,还是一个statement。而作为expression并且在一句代码的结尾处时,不要忘了后面的分号!(亲自踩坑,友情提醒)
To be continue...
可能你会觉得这些改进还是小修小改,不值得过分激动。但是,JEP 325是JEP 305: Pattern Matching的依赖。虽然没有最终确定,但或许Pattern Matching会在不久后的几个版本正式引入,到时又将是语言层面的大革命。后续的几个版本还是值得期待的。
尝鲜Java 12新特性:switch表达式的更多相关文章
- Java 12 新特性介绍,快来补一补
Java 12 早在 2019 年 3 月 19 日发布,它不是一个长久支持(LTS)版本.在这之前我们已经介绍过其他版本的新特性,如果需要可以点击下面的链接进行阅读. Java 11 新特性介绍 J ...
- Java 8 新特性——Lambdas 表达式
本文内容 引入 测试数据 collect(toList()) map filter flatMap max 和 min reduce 整合操作 参考资料 Java 8 对核心类库的改进主要包括集合类的 ...
- Java 8 新特性 - Lambda表达式
Lambda表达式 vs 匿名类既然lambda表达式即将正式取代Java代码中的匿名内部类,那么有必要对二者做一个比较分析.一个关键的不同点就是关键字 this.匿名类的 this 关键字指向匿名类 ...
- Java12新特性 -- switch表达式
传统switch表达式的弊端: 匹配是自上而下的,如果忘记写break, 后面的case语句不论匹配与否都会执行: 所有的case语句共用一个块范围,在不同的case语句定义的变量名不能重复: 不能在 ...
- Java 12 新特性
Java 12 已如期于 3 月 19 日正式发布,此次更新是 Java 11 这一长期支持版本发布之后的一次常规更新,截至目前,Java 半年为发布周期,并且不会跳票承诺的发布模式,已经成功运行一年 ...
- 尝鲜 vue3.x 新特性 - CompositionAPI
0. 基础要求 了解常见的 ES6 新特性 ES6 的导入导出语法 解构赋值 箭头函数 etc... 了解 vue 2.x 的基本使用 组件 常用的指令 生命周期函数 computed.watch.r ...
- Java 8新特性--Lambda表达式作为返回值
lambda表达式作为方法的返回值:
- Java13新特性 -- switch表达式动态CDS档案(动态类数据共享归档)
支持在Java application执行之后进行动态archive.存档类将包括默认的基础层CDS存档中不存在的所有已加载的应用程序和库类.也就是说,在Java 13中再使用AppCDS的时候,就不 ...
- Java13新特性 -- switch表达式
引入了yield语句,用于返回值: 和return的区别在于:return会直接跳出当前循环或者方法,而yield只会跳出当前switch块. @Test public void testSwitch ...
随机推荐
- Java设计模式小议之1------- 迭代器模式
定义:提供一种方法访问一个容器对象中各个元素,而又不暴露该对象的内部细节. 类型:行为类模式 这里用一个具体的案例来说明一下迭代器模式的简单使用 我们都知道在商店中,经常要把商品放到书架上,并将商品的 ...
- 【原】无脑操作:Windows下搭建Kafka运行环境
Kafka是一种高吞吐量的分布式发布订阅消息系统 1.优点:① 通过磁盘数据结构提供消息的持久化,这种结构对于即使数以TB的消息存储也能够保持长时间的稳定性能.② 高吞吐量:即使是非常普通的硬件Kaf ...
- cmd 配置dchp服务器
1.安装DHCP服务器角色,这样在netsh下才会有dhcp上下文 2.编写配置dhcp的脚本 从命令行运行netsh有两种语法: 比如要获取已经配置的网络接口列表 1.写全 netsh -r Rem ...
- Quartz实现分布式可动态配置的定时任务
关键词: 1. 定时任务 2. 分布式 3. 可动态配置触发时间 一般通过Quartz实现定时任务很简单.如果实现分布式定时任务需要结合分布式框架选择master节点触发也可以实现.但我们有个实际需求 ...
- GitHub的Repository权限将public转为private
2019年1月7日,GitHub CEO Nat Friedman 于官方博客公开发文,称“New year, new GitHub”,宣布从此将免费无限地为普通用户提供私有仓库服务. 因此,我们可以 ...
- 从壹开始前后端分离【 .NET Core2.0 +Vue2.0 】框架之四 || Swagger的使用 3.2
前言 如果想直接在域名的根目录直接加载 swagger 比如访问:localhost:8001 就能访问,可以这样设置: app.UseSwaggerUI(c => { c.SwaggerEnd ...
- FileSizeUtil【获取文件夹或文件的大小】
版权声明:本文为HaiyuKing原创文章,转载请注明出处! 前言 获取文件夹或者文件的大小,可以指定单位,也可以自动计算合适的单位值. 效果图 代码分析 常用的方法: getFolderOrFile ...
- 【Java】留下没有基础眼泪的面试题
前言 只有光头才能变强 本文力求简单讲清每个知识点,希望大家看完能有所收获 一.如何减少线程上下文切换 使用多线程时,不是多线程能提升程序的执行速度,使用多线程是为了更好地利用CPU资源! 程序在执行 ...
- SLAM+语音机器人DIY系列:(四)差分底盘设计——2.stm32主控软件设计
摘要 运动底盘是移动机器人的重要组成部分,不像激光雷达.IMU.麦克风.音响.摄像头这些通用部件可以直接买到,很难买到通用的底盘.一方面是因为底盘的尺寸结构和参数是要与具体机器人匹配的:另一方面是因为 ...
- k8s数据管理(八)--技术流ken
volume 我们经常会说:容器和 Pod 是短暂的.其含义是它们的生命周期可能很短,会被频繁地销毁和创建.容器销毁时,保存在容器内部文件系统中的数据都会被清除. 为了持久化保存容器的数据,可以使用 ...