record
Java 14 中预览的新特性叫做 Record 。
在 Java 中,Record 是一种特殊类型的 Java 类,可用来创建不可变类。
参考 JEP 395 。
Jackson 2.12 支持 Record 。
任何时候创建 Java 类,都会创建大量的样板代码:
- 每个字段的 set ,get 方法
- 公共的构造方法
- 重写 hashCode ,toString,equals 方法
Record 避免上述的样板代码,有如下特点: - 带有全部参数的构造方法
- public 访问器
- toString() ,hashCode() ,equals()
- 无 set ,get 方法,没有遵循 Bean 的命名规范
- final 类,不能继承 Record 。Record 为隐式的 final 类,除此之外与普通类一样
- 不可变类,通过构造创建 Record
- final 属性,不可修改
- 不能声明实例属性,能声明 static 成员
用法
- 创建 Student Record
public record Student(Integer id, String name, String email, Integer age) {}
- 创建 Record 对象
public static void main(String[] args) {
Student lisi = new Student(1001, "lisi", "lisi@qq.com", 20);
System.out.println("lisi = " + lisi.toString());
Student zhangsan = new Student(1002, "zhangsan", "lisi@qq.com", 20);
System.out.println("zhangsan = " + zhangsan.toString());
System.out.println("lisi.equals(zhangsan) = " + lisi.equals(zhangsan));
System.out.println("lisi.name() = " + lisi.name());
System.out.println("zhangsan.name() = " + zhangsan.name());
}
控制台输出:
lisi = Student[id=1001, name=lisi, email=lisi@qq.com, age=20]
zhangsan = Student[id=1002, name=zhangsan, email=lisi@qq.com, age=20]
lisi.equals(zhangsan) = false
lisi.name() = lisi
zhangsan.name() = zhangsan
Record 通过构造方法创建了只读的对象,能够读取每个属性,不能设置新的属性值。
Record 对每个属性提供了 public 访问器,例如 lisi.name() 。
实例方法
Record 是 Java 类,和普通 Java 类一样定义方法。我们创建普通的方法 concat ,将 name 和 age 连接为一个字符串输出。
- 创建实例方法
public record Student(Integer id, String name, String email, Integer age) {
public String concat() {
return String.format("姓名:%s,年龄是:%d", this.name, this.age);
}
}
- 调用实例方法
public static void main(String[] args) {
Student lisi = new Student(1001, "lisi", "lisi@qq.com", 20);
String nameAndAge = lisi.concat();
System.out.println(nameAndAge);
}
控制台输出:
姓名:lisi,年龄是:20
静态方法
Record 类定义静态方法,使用静态方法与普通类一样。
- 创建静态方法
public record Student(Integer id, String name, String email, Integer age) {
public String concat() {
return String.format("姓名:%s,年龄是:%d", this.name, this.age);
}
/** 静态方法 */
public static String emailUpperCase(String email) {
return Optional.ofNullable(email).orElse("no email").toUpperCase();
}
}
- 测试静态方法
public static void main(String[] args) {
String emailUpperCase = Student.emailUpperCase("lisi@163.com");
System.out.println("emailUpperCase = " + emailUpperCase);
}
构造方法
我们可以在 Record 中添加构造方法,有三种类型的构造方法:
- 紧凑型构造方法没有任何参数,甚至没有括号
- 规范构造方法是以所有成员作为参数
- 定制构造方法是自定义参数个数
- 紧凑和定制构造方法
public record Student(Integer id, String name, String email, Integer age) {
/*紧凑构造方法*/
public Student {
System.out.println("id"+ id );
if( id < 1 ){
throw new RuntimeException("ok");
}
}
/*自定义构造方法*/
public Student(Integer id, String name) {
this(id, name, null, null);
}
}
- 编译结果
public record Student(Integer id, String name, String email, Integer age) {
/** 紧凑构造方法和规范构造方法合并了 */
public Student(Integer id, String name, String email, Integer age) {
System.out.println("id" + id);
if (id < 1) {
throw new RuntimeException("ok"); }
else {
this.id = id;
this.name = name;
this.email = email;
this.age = age;
}
}
public Student(Integer id, String name) {
this(id, name, (String)null, (Integer)null);
}
}
接口
Java Record 可以与普通类一样实现接口,重写接口的方法。
- 创建新的接口,定义一个规范方法。
public interface PrintInterface {
/** 输出自定义描述信息 */
void print();
}
- 创建新的 Record 实现接口,重写接口的方法,实现当前 Record 有关的业务逻辑
public record ProductRecord(String id, String name, Integer qty) implements PrintInterface {
@Override
public void print() {
String productDesc = String.join("-", id, name, qty.toString());
System.out.println("商品信息 = " + productDesc);
}
}
- 测试 print 方法
public static void main(String[] args) {
ProductRecord product = new ProductRecord("P001", "手机", 100);
product.print();
}
局部 Record
Record 可以作为局部对象使用。在代码块中定义并使用 Record,下面定义一个 SaleRecord :
- 定义 Local Record
public static void main(String[] args) {
// 定义 Java Record
record SaleRecord(String saleId, String productName, Double money){}; // 创建 Local Record
SaleRecord saleRecord = new SaleRecord("S22020301", "手机", 3000.0); // 使用 SaleRecord
System.out.println("销售记录 = " + saleRecord.toString());
}
- 控制台输出
销售记录 = SaleRecord[saleId=S22020301, productName=手机, money=3000.0]
嵌套 Record
多个 Record 可以组合定义, 一个 Record 能够包含其他的 Record 。
我们定义 Record 为 Customer,存储客户信息,包含了 Address 和 PhoneNumber 两个 Record:
- 定义 Record
public record Address(String city, String address, String zipcode) {}
public record PhoneNumber(String areaCode, String number) {}
public record Customer(String id, String name, PhoneNumber phoneNumber, Address address) {}
- 创建 Customer 对象
public static void main(String[] args) {
Address address = new Address("北京", "大兴区凉水河二街-8号10栋三层", "100176");
PhoneNumber phoneNumber = new PhoneNumber("010", "400-8080-105");
Customer customer = new Customer("C1001", "李项", phoneNumber, address);
System.out.println("客户 = " + customer.toString());
}
- 控制台输出
客户 = Customer[id=C1001, name=李项, phoneNumber=PhoneNumber[areaCode=010, number=400-8080-105], address=Address[city=北京, address=大兴区凉水河二街 9 号 10 栋三层, zipcode=100176]]
instanceof 判断 Record 类型
instanceof 能够与 Java Record 一起使用。编译器知道记录组件的确切数量和类型。
- 声明 Person Record ,拥有两个属性 name 和 age
public record Person(String name, Integer age) {}
2. 在一个业务方法判断当是 Record 类型时,继续判断 age 年龄是否满足 18 岁
```java
public class SomeService {
public boolean isEligible(Object obj){
// 判断 obj 为 Person 记录类型
if( obj instanceof Person(String name, Integer age)){
return age >= 18;
}
return false;
}
}
instanceof 还可以下面的方式:
if( obj instanceof Person(String name, Integer age) person) {
return person.age() >= 18;
}
或者
if(obj instanceof Person p) {
return p.age() >= 18;
}
- 测试代码
public static void main(String[] args) {
SomeService service = new SomeService();
boolean flag = service.isEligible(new Person("李四", 20));
System.out.println("年龄符合吗?" + flag);
}
控制台输出:
控制台输出 flag 为 true
处理判断中的 Record 为 null
Java Record 能够自动处理 null 。
- record 为 null
public static void main(String[] args) {
SomeService service = new SomeService();
boolean eligible = service.isEligible(null);
System.out.println("年龄符合吗?" + eligible);
}
控制台输出 eligible 为 false ,Debug 调试代码,发现 if 语句判断为 false,不执行。
总结
- abstract 类 java.lang.Record 是所有 Record 的父类。
- 有对于 equals() ,hashCode() ,toString() 的定义说明
- Record 类能够实现 java.io.Serializable 序列化或反序列化
- Record 支持泛型,例如 record Gif
(T t) {} - java.lang.Class 类与 Record 类有关的两个方法:
boolean isRecord() : 判断一个类是否是 Record 类型
RecordComponent[] getRecordComponents():Record 的数组,表示此记录类的所有记录组件
Customer customer = new Customer(....);
RecordComponent[] recordComponents = customer.getClass().getRecordComponents();
for (RecordComponent recordComponent : recordComponents) {
System.out.println("recordComponent = " + recordComponent);
}
boolean record = customer.getClass().isRecord();
System.out.println("record = " + record);

浙公网安备 33010602011771号