New Java Feature - Record
概述
- JDK 16
- Java 档案类是用来表示不可变数据的透明载体
OOP
封装 + 继承 + 多态
接口不是多线程安全的 - 将 Public 方法设置成同步方法 - 开销很大
更优方案 - 即使不使用线程同步,也能做到多线程安全 - 不可变对象
- 天生的多线程安全 - 类对象一旦实例化就不能再修改
- 简化代码 - 删除读取半径的方法,直接公开半径这个变量 - 与 Go 类似
- Circle 一直可以用半径来表达,所以并没有带来违反封装原则的实质性后果
进一步简化
- 使用公开的只读变量 - 使用 final 修饰符来表明只读变量
- 公开的只读变量,只在在公开的构造方法中赋值 - 解决对象的初始化问题
- 公开的只读变量,替换掉了读取的方法 - 减少代码量
声明档案类
Java 档案类是用来表示不可变数据的透明载体
- record 关键字是 class 关键字的一种特殊表现形式,用来标识档案类
- record 关键字可以使用与 class 关键字差不多一样的类修饰符 - public/static
- 类标识符 Circle 后,用小括号括起来的参数 - 类似于一个构造方法
- 在大括号里,档案类的实现代码 - 变量的声明没有了,构造方法也没有了
- 类标识符声明后面的小括号里的参数,就是等价的不可变变量
- 在档案类中,这样的不可变变量是私有的变量,不可以直接读取
- 但可以通过等价的方法来调用,变量的标识符就是等价方法的标识符
- 档案类表示的不可变数据,除了构造方法之外,并没有给不可变变量赋值的方法
意料之外的改进
档案类内置了缺省的 equals 方法、hashCode 方法以及 toString 方法的实现 - 提高编码效率
- 如果需要比较两个实例是不是相等的,需要重写 equals 和 hashCode 方法 - 代码安全的重灾区
- 如果需要把实例转换为肉眼可以阅读的信息,需要重写 toString 方法
不可变的数据
为了强化不可变的原则,避免 OOP 的陷阱,档案类还做了如下限制
- Java 档案类不支持扩展子句,用户不能定制它的父类 - java.lang.Record
- 父类不能定制,意味着不能通过修改父类来影响 Java 档案类的行为
- Java 档案类是一个 final 类,不支持子类,也不能是抽象类
- 没有子类,意味着不能通过修改子类来改变 Java 档案类的行为
- Java 档案类声明的变量是不可变的变量
- 一旦实例化就不能再修改
- Java 档案类不能声明可变的变量,也不能支持实例初始化的方法
- 保证了只能使用档案类形式的构造方法,避免额外的初始化对可变性的影响
- Java 档案类不能声明 native 方法
- 如果运行 native 方法,意味着打开了修改不可变变量的后门
除了上述的限制,Java 档案类与普通类的用法是一样的
透明的载体
透明载体 - 档案类承载有缺省实现的方法,这些方法可以直接使用,也可以替换掉
- Java 档案类内置了下列方法的缺省实现
- 构造方法、equals 方法、hashCode 方法、toString 方法、不可变数据的读取方法
- 可以使用缺省的实现,也可以替换掉缺省的实现
- 除了构造方法,其它的替换方法都可以使用 Override 注解来标注
1 | public record Circle(double radius) implements Shape { |
重写构造函数
最常见的替换,是要在构造方法里对构造类声明的变量添加必要的检查
1 | public record Circle(double radius) implements Shape { |
- 构造方法的声明没有参数,也没有给实例变量赋值的语句
- 为了简化代码,Java 编译的时候,会补充上去
Convert canonical constructor to compact form
重写 equals 方法
- 如果缺省的 equals 方法和 hashCode 方法不能正常工作或者存在安全问题,就需要替换掉
- 如果声明的不可变变量没有重写 equals 方法和 hashCode 方法 - 行为可能不可预测
不可变的变量是一个数组 - 数组变量没有重写 equals 方法
1 | jshell> record Password(byte[] password) {}; |
java.lang.String - 重写了 equals 方法
1 | jshell> record Password(String password) {}; |
一般情况下,equals 方法和 hashCode 方法是成双成对的
不推荐的重写
- 通常不建议重写不可变数据的读取方法
- 可能变更缺省的不可变数值,打破实例的状态,造成很多无法预料的后果
容易出错 + 难以调试
1 | jshell> record Number(int x) { |
All articles in this blog are licensed under CC BY-NC-SA 4.0 unless stating additionally.