Java枚举类enum;记录类Record;密封类Sealed、permits
文章目录
- 一、枚举类
- 2. 在 Person 类中使用枚举
- 3. 使用示例
- 二、Java 14 记录类 (Record Classes)
- 什么是记录类
- 基本语法
- 主要特性
- 自定义行为
- 使用示例
- 限制
- 适用场景
- 三、Java 17 密封类型 (Sealed Classes)
- 一、密封类型核心概念
- 1. 设计目的
- 2. 基本组成
- 3. 三种继承约束
- 二、规则
- 1. 声明密封类
- 2. 声明密封接口
- 3. 子类要求
- 4. 特殊规则
- image-20250727155543950
- 模式匹配与密封类
- 实际示例
- 基本密封类
- 带密封子类的层次结构
- 密封接口
- 典型使用场景
- 1. 代数数据类型(ADT)
- 2. 状态机实现
- 3. UI组件层次
- 与模式匹配的完美结合
- 1. switch 表达式
- 2. instanceof 模式匹配
- 编译时检查
- 密封类的优点
- 注意事项
一、枚举类
下面是一个简单的枚举类示例,展示了如何在Java中定义和使用枚举:
/*** 性别枚举类*/
public enum Gender {MALE("男"),FEMALE("女");private final String description;// 构造方法Gender(String description) {this.description = description;}// 获取描述信息public String getDescription() {return description;}// 重写toString方法@Overridepublic String toString() {return this.name() + "(" + description + ")";}
}
这个枚举类的特点包括:
- 基本定义:使用
enum
关键字定义枚举类Gender
- 枚举值:包含两个枚举常量
MALE
和FEMALE
- 成员变量:每个枚举值都有一个对应的描述信息
- 构造方法:私有构造方法,用于初始化描述信息
- 公共方法:提供获取描述信息的方法和重写的
toString
方法
基于您提供的 [Person.java](file://C:\Users\win\IdeaProjects\untitled2\src\Person.java) 文件,我将展示如何使用枚举类。首先,让我创建一个枚举类,然后演示如何在 Person
类中使用它。
2. 在 Person 类中使用枚举
// Person.java
public class Person {private String name;private int age;private Gender gender;// 默认构造函数public Person() {}// 带参数的构造函数public Person(String name, int age, Gender gender) {this.name = name;this.age = age;this.gender = gender;}// getter 和 setter 方法public String getName() {return name;}public void setName(String name) {this.name = name;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}public Gender getGender() {return gender;}public void setGender(Gender gender) {this.gender = gender;}// toString 方法@Overridepublic String toString() {return "Person{" +"name='" + name + '\'' +", age=" + age +", gender=" + gender +'}';}
}
3. 使用示例
// 使用示例
public class Main {public static void main(String[] args) {// 创建 Person 对象并使用枚举Person person1 = new Person("张三", 25, Gender.MALE);Person person2 = new Person("李四", 30, Gender.FEMALE);System.out.println(person1);System.out.println(person2);// 获取枚举描述System.out.println("性别描述: " + Gender.MALE.getDescription());// 遍历所有枚举值System.out.println("所有性别选项:");for (Gender g : Gender.values()) {System.out.println(g + " - " + g.getDescription());}}
}
这样使用枚举的好处包括:
- 类型安全:编译时检查,避免无效值
- 代码可读性:
Gender.MALE
比数字或字符串更清晰 - 易于维护:枚举值集中管理
- 功能丰富:可以添加方法和属性
二、Java 14 记录类 (Record Classes)
Java 14 引入了记录类(Record Classes)作为预览特性,这是一种新的类声明形式,旨在简化不可变数据载体的创建。
什么是记录类
记录类是一种特殊的类,主要用于存储不可变数据。它自动提供以下功能:
- 字段的自动声明(基于记录头中的组件)
- 规范的构造函数
- 自动生成的
get
、equals()
、hashCode()
和toString()
方法
基本语法
public record Point(int x, int y) {}
这个简单的声明等价于以下常规类:
public final class Point {private final int x;private final int y;public Point(int x, int y) {this.x = x;this.y = y;}public int x() { return x; }public int y() { return y; }@Overridepublic boolean equals(Object o) { ... }@Overridepublic int hashCode() { ... }@Overridepublic String toString() { ... }
}
主要特性
- 不可变性:记录类的所有字段都是 final 的
- 简洁语法:大大减少了样板代码
- 自动方法:自动生成访问器、equals、hashCode 和 toString
- 访问器方法:访问器方法与字段同名(如
x()
而不是getX()
) - final 类:记录类隐式是 final 的,不能被继承
自定义行为
虽然记录类自动生成许多方法,但你可以自定义它们:
public record Point(int x, int y) {// 紧凑构造函数(无需声明参数或赋值)public Point {if (x < 0 || y < 0) {throw new IllegalArgumentException("Coordinates must be non-negative");}}// 自定义方法public double distanceFromOrigin() {return Math.sqrt(x*x + y*y);}// 也可以覆盖自动生成的方法@Overridepublic String toString() {return String.format("Point[x=%d, y=%d]", x, y);}
}
使用示例
public class Main {public static void main(String[] args) {Point p1 = new Point(10, 20);Point p2 = new Point(10, 20);System.out.println(p1.x()); // 10System.out.println(p1.y()); // 20System.out.println(p1.equals(p2)); // trueSystem.out.println(p1); // Point[x=10, y=20]}
}
限制
- 记录类不能扩展其他类(隐含 final)
- 记录类的字段不能单独声明(必须在记录头中声明)
- 记录类不能是抽象的
- 记录类的组件(字段)是隐式 final 的
适用场景
记录类最适合用于:
- DTO(数据传输对象)
- 值对象
- 简单的数据聚合
- 方法返回多个值的场景
三、Java 17 密封类型 (Sealed Classes)
密封类是一种限制哪些其他类或接口可以继承或实现它的类或接口。这是对 Java 继承模型的增强,允许开发者明确控制类的层次结构。
一、密封类型核心概念
在传统 Java 中:
final
类:完全不能被继承- 非
final
类:可以被任何类继承
密封类提供了中间选项:可以被特定的一些类继承,但不是所有类都能继承它。
1. 设计目的
- 允许类/接口作者精确控制哪些类可以继承或实现它们
- 解决传统继承模型中"要么完全开放,要么完全封闭"的限制
- 为模式匹配提供更好的支持
2. 基本组成
- 密封类/接口:使用
sealed
修饰的主类 - permits 子句:明确指定允许的子类
- 子类修饰符:必须为
final
,sealed
或non-sealed
3. 三种继承约束
public sealed class Shape permits Circle, Square, Rectangle {}// 1. final: 不能再被继承
public final class Circle extends Shape {} // 2. sealed: 可以继续限制子类
public sealed class Rectangle extends Shape permits FilledRectangle {}// 3. non-sealed: 重新开放继承
public non-sealed class Square extends Shape {}
二、规则
1. 声明密封类
[访问修饰符] sealed class 类名 [extends 父类] [implements 接口...]permits 子类列表 {// 类体
}
sealed
修饰符:标记类为密封类permits
子句:指定允许继承的子类- 子类要求:所有允许的子类必须满足以下条件之一:
final
:不能再被继承sealed
:可以进一步限制继承non-sealed
:开放继承(恢复传统 Java 行为)
2. 声明密封接口
[访问修饰符] sealed interface 接口名 permits 实现类列表 {// 接口体
}
3. 子类要求
- 必须在同一模块(或未命名模块的同一包)中
- 必须直接继承密封类
- 必须显式声明为以下三种之一:
final
:终止继承sealed
:继续密封non-sealed
:开放继承
4. 特殊规则
- 如果子类与密封类在同一文件中,可省略
permits
- 密封类和其子类应有相同的包访问权限
- 记录类(record)可以作为密封类的子类
模式匹配与密封类
密封类与 switch 表达式和模式匹配完美配合:
public double area(Shape shape) {return switch (shape) {case Circle c -> Math.PI * c.radius() * c.radius();case Square s -> s.side() * s.side();// 不需要default分支,因为Shape是密封的且所有子类都已处理};
}
实际示例
基本密封类
public sealed class Shape permits Circle, Square {public abstract double area();
}public final class Circle extends Shape {private final double radius;public Circle(double radius) { this.radius = radius; }@Overridepublic double area() {return Math.PI * radius * radius;}
}public final class Square extends Shape {private final double side;public Square(double side) { this.side = side; }@Overridepublic double area() {return side * side;}
}
带密封子类的层次结构
public sealed class Expressionpermits ConstantExpr, PlusExpr, TimesExpr, NegExpr {// ...
}public final class ConstantExpr extends Expression {private final int value;// ...
}public final class PlusExpr extends Expression {private final Expression left, right;// ...
}public sealed class TimesExpr extends Expressionpermits DivExpr {// ...
}public final class DivExpr extends TimesExpr {// ...
}public non-sealed class NegExpr extends Expression {// 可以被任意继承
}
密封接口
密封概念同样适用于接口:
public sealed interface Tree permits BinaryTree, Leaf {// ...
}public record Leaf(Object value) implements Tree {}public record BinaryTree(Tree left, Tree right) implements Tree {}
典型使用场景
1. 代数数据类型(ADT)
public sealed interface Exprpermits Constant, Add, Subtract, Multiply, Divide {double eval();
}public record Constant(double value) implements Expr {public double eval() { return value; }
}public record Add(Expr left, Expr right) implements Expr {public double eval() { return left.eval() + right.eval(); }
}// 其他运算类似...
2. 状态机实现
public sealed interface TrafficLightpermits Red, Yellow, Green, FlashingYellow {TrafficLight next();
}public final class Red implements TrafficLight {public TrafficLight next() { return new Green(); }
}public final class Green implements TrafficLight {public TrafficLight next() { return new Yellow(); }
}// 其他状态...
3. UI组件层次
public sealed class UIControlpermits Button, TextField, Slider, ComboBox {private String id;public abstract void render();
}public final class Button extends UIControl {public void render() { /* 按钮渲染逻辑 */ }
}public non-sealed class Slider extends UIControl {public void render() { /* 滑块渲染逻辑 */ }
}
与模式匹配的完美结合
1. switch 表达式
public String getShapeInfo(Shape shape) {return switch (shape) {case Circle c -> "圆形,半径: " + c.radius();case Square s -> "正方形,边长: " + s.side();case Rectangle r -> "矩形,面积: " + (r.length() * r.width());// 不需要default,编译器知道所有可能性};
}
2. instanceof 模式匹配
if (shape instanceof Circle c) {System.out.println("圆面积: " + c.area());
} else if (shape instanceof Square s) {System.out.println("正方形周长: " + s.perimeter());
}
编译时检查
Java 编译器会对密封类进行严格检查:
- 所有允许的子类必须可访问
- 每个允许的子类必须直接扩展密封类
- 每个允许的子类必须使用
final
、sealed
或non-sealed
修饰 permits
子句中的类必须存在
密封类的优点
- 增强代码可维护性:明确知道哪些类可以继承父类
- 改进模式匹配:编译器可以验证是否处理了所有情况
- 更好的领域建模:精确表示有限的子类型集合
- 提高安全性:防止未知子类的出现
注意事项
- 密封类及其子类通常应在同一模块中,或如果在不同模块中,则必须在同一包中
- 密封类的子类必须有规范的构造函数
- 反射 API 已更新以支持密封类检查
密封类是 Java 向更严格的类型系统和更好的模式匹配支持迈出的重要一步,特别适合需要精确控制类层次结构的领域建模场景。