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 注意事项

  1. 预览特性:模式匹配在 JDK 17 中是预览特性,需要添加编译和运行参数:

    javac --enable-preview --release 17 Main.java
    java --enable-preview Main
    
  2. 全面性检查:switch 表达式必须覆盖所有可能情况,或者有 default 分支。

  3. 类型推断: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 表达式主要改进:

  1. 箭头语法:更简洁,无需 break
  2. 可以作为表达式:直接返回值
  3. 模式匹配:支持类型检查和条件判断
  4. 空值处理:可以显式处理 null
  5. 全面匹配:编译器会检查是否覆盖所有情况
  6. 与记录类、密封类完美配合:提供更好的类型安全

这些改进使 switch 更强大、更安全、更易读,是 Java 现代化的重要一步。

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐