switch 表达式 switch 表达式这个特性在 JDK14 版本中正式发布。在以往 switch 只有代码块,如今通过 switch 计算的值可以作为表达式,我们通过计算一个月份有多少天这个经典案例来了解它。
传统的代码实现如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 public int getCountByOldWay (LocalDateTime dateTime) { int dayCount; switch (dateTime.getMonth()) { case JANUARY: case MARCH: case MAY: case JULY: case AUGUST: case OCTOBER: case DECEMBER: dayCount = 31 ; break ; case APRIL: case JUNE: case SEPTEMBER: case NOVEMBER: dayCount = 30 ; break ; case FEBRUARY: dayCount = Year.isLeap(dateTime.getYear()) ? 29 : 28 ; break ; default : throw new IllegalArgumentException ("Invalid month" ); } return dayCount; }
使用 switch 表达式,代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 public int getCountByNewExpression (LocalDateTime dateTime) { return switch (dateTime.getMonth()) { case JANUARY, MARCH, MAY, JULY, AUGUST, OCTOBER, DECEMBER -> 31 ; case APRIL, JUNE, SEPTEMBER, NOVEMBER -> 30 ; case FEBRUARY -> { if (Year.isLeap(dateTime.getYear())) { yield 29 ; } else { yield 28 ; } } }; }
上面的代码中,使用了新的关键字 yield。可以把 yield 语句产生的值看成是 switch 表达式的返回值,因此 yield 只能用在 switch 表达式里,而不能用在 switch 语句里。
1 2 3 4 5 6 7 8 9 10 public int getCountByNewExpression2 (LocalDateTime dateTime) { return switch (dateTime.getMonth()) { case JANUARY, MARCH, MAY, JULY, AUGUST, OCTOBER, DECEMBER -> 31 ; case APRIL, JUNE, SEPTEMBER, NOVEMBER -> 30 ; case FEBRUARY -> Year.isLeap(dateTime.getYear()) ? 29 : 28 ; }; }
使用 switch 语句,代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 public int getCountByNewStatement (LocalDateTime dateTime) { int dayCount = 0 ; switch (dateTime.getMonth()) { case JANUARY, MARCH, MAY, JULY, AUGUST, OCTOBER, DECEMBER -> dayCount = 31 ; case APRIL, JUNE, SEPTEMBER, NOVEMBER -> dayCount = 30 ; case FEBRUARY -> dayCount = (Year.isLeap(dateTime.getYear()) ? 29 : 28 ); } return dayCount; }
switch 模式匹配 switch 模式匹配这个特性在 JDK21 版本中正式发布。之前 switch 匹配的数据可以是数字、枚举、字符串,现在增加了对引用类型的支持。
借助这个特性,我们可以优雅地解决子类扩充出现的兼容性问题,降低代码的维护难度,提高多情景处理的性能。
假设有这样一个场景:有一个定义形状的接口 Shape,在 1.0 版本的代码中有接口 Shape 的 2 个实现类(长方形类 Rectangle、正方形类 Square),以及一个工具类 ShapeUtils(主要提供计算面积的方法);2.0 版本的代码中增加了 1 个新的实现类(圆形类 Circle),但实际情况中我们可能会忘记去对工具类 ShapeUtils 进行扩展,那能否借助某种机制在第一时间帮我们发现这类问题呢?答案是 switch 的模式匹配。
接口及其实现类的代码如下(注意:Circle 类在 2.0 版本才引入):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 public sealed interface Shape permits Shape.Rectangle, Shape.Square, Shape.Circle { record Rectangle (double length, double width) implements Shape { } record Square (double length) implements Shape { } record Circle (double radius) implements Shape { } }
1.0 版本的 ShapeUtils 工具类的代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 import java.util.Objects;public class ShapeUtils { public static double calcArea (Shape shape) { if (Objects.isNull(shape)) { throw new IllegalArgumentException ("shape is null" ); } double area = 0 ; if (shape instanceof Shape.Rectangle rectangle) { area = rectangle.length() * rectangle.width(); } else if (shape instanceof Shape.Square square) { area = square.length() * square.length(); } return area; } }
如果未对 ShapeUtils 中的 calcArea() 方法进行扩展,那么计算 Circle 类的面积会返回数值 0,实际上忘记对其实现也很正常,往往出现了线上问题我们才可能发现。通过 switch 的模式匹配可以很好地解决这个问题:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 import java.util.Objects;public class ShapeUtils { public static double calcArea (Shape shape) { return switch (shape) { case null -> throw new RuntimeException ("shape is null" ); case Shape.Rectangle rectangle -> rectangle.length() * rectangle.width(); case Shape.Square square -> square.length() * square.length(); case Shape.Circle circle -> Math.PI * (circle.radius() * circle.radius()); }; } }