JDK 17 中 switch 的全面用法详解
·
JDK 17 中的 switch 表达式相比传统 switch 语句有了重大改进,支持更简洁的语法、模式匹配等新特性。
1. 基础 switch 表达式(JDK 14+)
1.1 箭头语法(Arrow Syntax)
// 传统 switch 语句
String dayType;
switch (day) {
case MONDAY:
case TUESDAY:
case WEDNESDAY:
case THURSDAY:
case FRIDAY:
dayType = "工作日";
break;
case SATURDAY:
case SUNDAY:
dayType = "周末";
break;
default:
dayType = "未知";
}
// JDK 14+ switch 表达式(箭头语法)
String dayType = switch (day) {
case MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY -> "工作日";
case SATURDAY, SUNDAY -> "周末";
default -> "未知";
};
1.2 多值匹配
int month = 3;
String season = switch (month) {
case 12, 1, 2 -> "冬季";
case 3, 4, 5 -> "春季";
case 6, 7, 8 -> "夏季";
case 9, 10, 11 -> "秋季";
default -> "无效月份";
};
System.out.println("季节: " + season); // 春季
1.3 使用 yield 返回值
// 当需要多行代码时使用 yield
int dayNumber = 2;
String result = switch (dayNumber) {
case 1 -> "周一";
case 2 -> {
System.out.println("今天是周二");
System.out.println("需要开会");
yield "周二";
}
case 3 -> "周三";
default -> "其他";
};
System.out.println(result);
2. 模式匹配(预览特性 - JDK 17)
2.1 类型模式匹配
// 需要启用预览特性:--enable-preview
Object obj = "Hello Java 17";
String result = switch (obj) {
case Integer i -> "整数: " + i;
case Long l -> "长整数: " + l;
case Double d -> "浮点数: " + d;
case String s -> "字符串: " + s;
case null -> "空值";
default -> "未知类型: " + obj.getClass().getName();
};
System.out.println(result); // 字符串: Hello Java 17
2.2 守卫条件(Guard Pattern)
Object obj = 42;
String result = switch (obj) {
case Integer i && i > 0 && i < 100 -> {
System.out.println("正整数且小于100");
yield "小整数: " + i;
}
case Integer i -> "整数: " + i;
case String s && !s.isEmpty() -> "非空字符串: " + s;
case String s -> "空字符串";
default -> "其他";
};
System.out.println(result); // 小整数: 42
2.3 嵌套模式匹配
record Point(int x, int y) {}
record Line(Point start, Point end) {}
record Rectangle(Point topLeft, Point bottomRight) {}
Object shape = new Rectangle(new Point(0, 0), new Point(10, 10));
String description = switch (shape) {
case Point p -> "点: (" + p.x() + ", " + p.y() + ")";
case Line(Point s, Point e) ->
"线段: (" + s.x() + "," + s.y() + ") -> (" + e.x() + "," + e.y() + ")";
case Rectangle(Point tl, Point br) ->
"矩形: [" + tl.x() + "," + tl.y() + "] - [" + br.x() + "," + br.y() + "]";
default -> "未知形状";
};
3. 全面匹配(Exhaustiveness)
3.1 密封类(Sealed Classes)与 switch
// 定义密封类
sealed interface Shape permits Circle, Rectangle, Triangle {
double area();
}
record Circle(double radius) implements Shape {
@Override
public double area() {
return Math.PI * radius * radius;
}
}
record Rectangle(double length, double width) implements Shape {
@Override
public double area() {
return length * width;
}
}
record Triangle(double base, double height) implements Shape {
@Override
public double area() {
return 0.5 * base * height;
}
}
// switch 表达式必须覆盖所有情况
Shape shape = new Circle(5.0);
String description = switch (shape) {
case Circle c -> "圆形,半径: " + c.radius() + ",面积: " + c.area();
case Rectangle r -> "矩形,长: " + r.length() + ",宽: " + r.width();
case Triangle t -> "三角形,底: " + t.base() + ",高: " + t.height();
// 不需要 default,因为 Shape 的所有子类都已覆盖
};
3.2 枚举的全面匹配
enum Status {
PENDING, PROCESSING, COMPLETED, FAILED
}
Status status = Status.PROCESSING;
// 处理所有枚举值,无需 default
String message = switch (status) {
case PENDING -> "待处理";
case PROCESSING -> "处理中";
case COMPLETED -> "已完成";
case FAILED -> "失败";
};
// 如果有新增枚举值,编译会报错,提示需要处理新情况
4. 空值处理
String input = null;
// JDK 17 可以显式处理 null
String result = switch (input) {
case null -> "输入为空";
case String s when s.isEmpty() -> "空字符串";
case String s when s.length() < 5 -> "短字符串";
case String s -> "长字符串: " + s;
};
System.out.println(result); // 输入为空
5. 综合实战示例
5.1 数据验证器
public class DataValidator {
public ValidationResult validate(Object data) {
return switch (data) {
case null ->
ValidationResult.error("数据不能为空");
case String s when s.trim().isEmpty() ->
ValidationResult.error("字符串不能为空或空白");
case String s when s.length() > 100 ->
ValidationResult.error("字符串长度不能超过100个字符");
case String s ->
ValidationResult.success(s);
case Integer i when i < 0 ->
ValidationResult.error("整数不能为负数");
case Integer i when i > 1000 ->
ValidationResult.error("整数不能超过1000");
case Integer i ->
ValidationResult.success(i);
case List<?> list when list.isEmpty() ->
ValidationResult.error("列表不能为空");
case List<?> list when list.size() > 50 ->
ValidationResult.error("列表元素不能超过50个");
case List<?> list ->
ValidationResult.success(list);
default ->
ValidationResult.error("不支持的数据类型: " + data.getClass());
};
}
record ValidationResult(boolean success, String message, Object data) {
static ValidationResult success(Object data) {
return new ValidationResult(true, "验证通过", data);
}
static ValidationResult error(String message) {
return new ValidationResult(false, message, null);
}
}
}
5.2 HTTP 响应处理器
public class HttpResponseHandler {
public void handleResponse(Object response) {
String result = switch (response) {
case HttpResponse.Success(var data) ->
processSuccess(data);
case HttpResponse.Error(var code, var message) when code >= 500 ->
processServerError(code, message);
case HttpResponse.Error(var code, var message) when code >= 400 ->
processClientError(code, message);
case HttpResponse.Error(var code, var message) ->
processOtherError(code, message);
case null ->
"响应为空";
default ->
"未知响应类型: " + response.getClass();
};
System.out.println(result);
}
private String processSuccess(Object data) {
return "成功: " + data;
}
private String processServerError(int code, String message) {
return "服务器错误 (" + code + "): " + message;
}
private String processClientError(int code, String message) {
return "客户端错误 (" + code + "): " + message;
}
private String processOtherError(int code, String message) {
return "其他错误 (" + code + "): " + message;
}
}
// 密封接口
sealed interface HttpResponse permits HttpResponse.Success, HttpResponse.Error {
record Success(Object data) implements HttpResponse {}
record Error(int code, String message) implements HttpResponse {}
}
5.3 表达式计算器
public class ExpressionCalculator {
sealed interface Expr permits Constant, Add, Subtract, Multiply, Divide {
double evaluate();
}
record Constant(double value) implements Expr {
@Override
public double evaluate() {
return value;
}
}
record Add(Expr left, Expr right) implements Expr {
@Override
public double evaluate() {
return left.evaluate() + right.evaluate();
}
}
record Subtract(Expr left, Expr right) implements Expr {
@Override
public double evaluate() {
return left.evaluate() - right.evaluate();
}
}
record Multiply(Expr left, Expr right) implements Expr {
@Override
public double evaluate() {
return left.evaluate() * right.evaluate();
}
}
record Divide(Expr left, Expr right) implements Expr {
@Override
public double evaluate() {
return left.evaluate() / right.evaluate();
}
}
// 使用模式匹配打印表达式树
public String formatExpression(Expr expr) {
return switch (expr) {
case Constant(var value) -> String.valueOf(value);
case Add(var left, var right) ->
"(" + formatExpression(left) + " + " + formatExpression(right) + ")";
case Subtract(var left, var right) ->
"(" + formatExpression(left) + " - " + formatExpression(right) + ")";
case Multiply(var left, var right) ->
formatExpression(left) + " * " + formatExpression(right);
case Divide(var left, var right) ->
formatExpression(left) + " / " + formatExpression(right);
};
}
// 使用示例
public static void main(String[] args) {
ExpressionCalculator calculator = new ExpressionCalculator();
// 表达式: (2 + 3) * 4
Expr expr = new Multiply(
new Add(new Constant(2), new Constant(3)),
new Constant(4)
);
System.out.println("表达式: " + calculator.formatExpression(expr));
System.out.println("结果: " + expr.evaluate()); // 20.0
}
}
6. 性能优化建议
6.1 使用字符串 switch
// JDK 7+ 支持字符串 switch
String command = "SAVE";
int priority = switch (command) {
case "SAVE", "UPDATE" -> 1; // 高优先级
case "QUERY", "SEARCH" -> 2; // 中优先级
case "LOG", "BACKUP" -> 3; // 低优先级
default -> 0; // 默认优先级
};
6.2 编译时常量
// 使用编译时常量优化
static final int MODE_HIGH = 1;
static final int MODE_MEDIUM = 2;
static final int MODE_LOW = 3;
int mode = MODE_HIGH;
String description = switch (mode) {
case MODE_HIGH -> "高性能模式";
case MODE_MEDIUM -> "平衡模式";
case MODE_LOW -> "节能模式";
default -> "未知模式";
};
7. 注意事项和最佳实践
7.1 注意事项
-
预览特性:模式匹配在 JDK 17 中是预览特性,需要添加编译和运行参数:
javac --enable-preview --release 17 Main.java java --enable-preview Main -
全面性检查:switch 表达式必须覆盖所有可能情况,或者有 default 分支。
-
类型推断:switch 表达式的类型由各个分支的类型推断决定。
7.2 最佳实践
public class SwitchBestPractices {
// 1. 优先使用 switch 表达式而非语句
public String getDayType(Day day) {
return switch (day) {
case MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY -> "工作日";
case SATURDAY, SUNDAY -> "周末";
};
}
// 2. 复杂逻辑提取到方法中
public void processData(Object data) {
String result = switch (data) {
case String s -> processString(s);
case Integer i -> processInteger(i);
case List<?> list -> processList(list);
default -> processDefault(data);
};
System.out.println(result);
}
private String processString(String s) {
return "字符串: " + s.length() + " 个字符";
}
private String processInteger(Integer i) {
return "整数: " + (i * 2);
}
private String processList(List<?> list) {
return "列表大小: " + list.size();
}
private String processDefault(Object obj) {
return "默认处理: " + obj.getClass().getSimpleName();
}
// 3. 使用记录模式简化代码
record User(String name, int age, String email) {}
public String validateUser(Object obj) {
return switch (obj) {
case User(var name, var age, var email) when age < 0 ->
"年龄不能为负数";
case User(var name, var age, var email) when age < 18 ->
name + " 未成年,需要监护人同意";
case User(var name, var age, var email) when !email.contains("@") ->
"邮箱格式不正确";
case User(var name, var age, var email) ->
"用户验证通过: " + name;
default ->
"不是有效的用户对象";
};
}
}
总结
JDK 17 中的 switch 表达式主要改进:
- 箭头语法:更简洁,无需 break
- 可以作为表达式:直接返回值
- 模式匹配:支持类型检查和条件判断
- 空值处理:可以显式处理 null
- 全面匹配:编译器会检查是否覆盖所有情况
- 与记录类、密封类完美配合:提供更好的类型安全
这些改进使 switch 更强大、更安全、更易读,是 Java 现代化的重要一步。
更多推荐


所有评论(0)