Java Record
Java14 中预览的新特性叫做 Record,在 Java 中,Record 是一种特殊类型的 Java 类。可用来创建不可变类,语法 简短。参考JEP 395. Jackson 2.12 支持 Record 类
任何时候创建 Java 类,都会创建大量的样板代码,我们可能做如下:
- 每个字段的 set,get 方法
- 公共的构造方法
- 重写 hashCode, toString(), equals()方法
Java Record 避免上述的样板代码,如下特点:
- 带有全部参数的构造方法
- public 访问器
- toString(),hashCode(),equals()
- 无 set,get 方法。没有遵循 Bean 的命名规范
- final 类,不能继承 Record,Record 为隐士的 final 类。除此之外与普通类一样
- 不可变类,通过构造创建 Record
- final 属性,不可修改
- 不能声明实例属性,能声明 static 成员
Record使用
IDEA 新建 Class,选择类 Record
step1: 创建 Student Record
代码语言:javascript复制public record Student(Integer id,String name,String email,Integer age) {
}
step2:创建 Record 对象
代码语言:javascript复制public void test(String[] args) {
//创建Record对象
Student lisi = new Student(1001,"lisi","lisi@qq.com",20);
System.out.println("lisi = " lisi);
//public方法,获取属性值,只读,没有set,get方法
Integer id = lisi.id();
String name = lisi.name();
String email = lisi.email();
Integer age = lisi.age();
System.out.println("id = " id);
}
现在能查看控制台输出:
Record 通过构造方法创建了只读的对象,能够读取每个属性,不能设置新的属性值。 Record 用于创建不可变的对象,同时减少了样板代码。Record 对每个属性提供了 public 访问器,例如 lisi.name()。
Instance Methods
Record 是 Java 类,和普通 Java 类一样定义方法。下面定义方法 concat,将姓名和年龄一起打印输出。 我们创建普通的方法 concat,将 name 和 age 连接为一个字符串输出。 step1:创建实例方法
代码语言:javascript复制public record Student(Integer id,String name,String email,Integer age) {
public String concat(){
return String.format("姓名:%s,年龄是:%d", this.name,this.age);
}
}
step2: 调用实例方法
代码语言:javascript复制@Test
public void test02() {
Student student = new Student(1001, "lisi", "lisi@qq.com", 20);
//使用对象,调用实例方法
String str = student.concat();
System.out.printf("str =" str);
}
最后控制台输出:
静态方法 Static Method
Record 类定义静态方法,试用静态方法与普通类一样。 step1: 创建静态方法
代码语言:javascript复制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();
}
}
step2:测试静态方法
代码语言:javascript复制@Test
public void test03() {
//使用类,静态方法
String email = Student.emailToUppreCase("lisi@qq.com");
System.out.println("eamil = " email);
}
Record 的构造方法
我们可以在 Record 中添加构造方法, 有三种类型的构造方法分别:是紧凑的,规范的和定制构造方法
- 紧凑型构造方法没有任何参数,甚至没有括号。
- 规范构造方法是以所有成员作为参数
- 定制构造方法是自定义参数个数
step1: 紧凑和定制构造方法
代码语言:javascript复制public record Student(Integer id,String name,String email,Integer age) {
/*紧凑构造方法*/
public Student{
System.out.println("id = " id);
if(id<1){
throw new RuntimeException("id<1");
}
}
/*自定义构造方法*/
public Student(Integer id,String name){
this(id,name,null,null);
}
}
测试:
代码语言:javascript复制 @Test
public void test04() {
Student student = new Student(1001,"lisi");
System.out.println("student = " student);
}
查看编译后的class文件(紧凑构造方法和规范构造方法合并了 ):
Record 与 与 Lombok
- Java Record 是创建不可变类且减少样板代码的好方法。Lombok 是一种减少样板代码的工具。两者有表面上的重叠部分。可能有人会说 Java Record 会代替 Lombok. 两者是有不同用途的工具。
- Lombok 提供语法的便利性,通常预装一些代码模板,根据您加入到类中的注解自动执行代码模板。这样的库纯粹是为了方便实现 POJO 类。通过预编译代码。将代码的模板加入到 class 中。
- Java Record 是语言级别的,一种语义特性,为了建模而用,数据聚合。简单说就是提供了通用的数据类,充当“数据载体",用于在类和应用程序之间进行数据传输。
Record 实现接口
Java Record 可以与普通类一样实现接口,重写接口的方法。
step1: 创建新的接口,定义一个规范方法。
代码语言:javascript复制public interface PrintInterface {
//输出自定义信息
void print();
}
step2: 创建新的 Record 实现接口,重写接口的方法,实现当前 Record 有关的业务逻辑
代码语言:javascript复制public record ProductRecord(Integer id, String name, Integer qty) implements PrintInterface {
@Override
public void print() {
StringJoiner joiner = new StringJoiner("-");
StringJoiner s = joiner.add(id.toString()).add(name).add(qty.toString());
System.out.println("商品信息 = " s);
}
}
ProductRecord 实现 print()方法,打印商品详情。
step3:测试 print 方法
代码语言:javascript复制 @Test
public void test05() {
ProductRecord productRecord = new ProductRecord(1001,"手机",200);
productRecord.print();
}
Local Record
Record 可以作为局部对象使用。在代码块中定义并使用 Record,下面定义一个 SaleRecord step1:定义 Local Record
代码语言:javascript复制public static void main(String[] args) {
//定义 Java Record
record SaleRecord(String saleId,String ProductName,Double money){};
//创建 Local Record
SaleRecord saleRecord = new SaleRecord("S001","显示器",3000.01);
//使用 SaleRecord
System.out.println("saleRecord = " saleRecord);
}
控制台输出:
嵌套 Record
多个 Record 可以组合定义, 一个 Record 能够包含其他的 Record。 我们定义 Record 为 Customer,存储客户信息,包含了 Address 和 PhoneNumber 两个 Record step1:定义 Record
代码语言:javascript复制public record Address(String city,String address,String zipCode) {
}
代码语言:javascript复制public record Customer(String id, String name, PhoneNumber phoneNumber, Address address) {
}
代码语言:javascript复制public record PhoneNumber(String areaCode,String number) {
}
step2: 创建 Customer 对象
代码语言:javascript复制@Test
public void test07() {
Address address = new Address("大连","旅顺口区兴发路216号","10010");
PhoneNumber phoneNumber = new PhoneNumber("010","400-8080-105");
Customer customer = new Customer("C1001","李四",phoneNumber,address);
System.out.println("customer = " customer);
String number = customer.phoneNumber().number();
System.out.println("number = " number);
String address1 = customer.address().address();
System.out.println("address1 = " address1);
}
控制台输出:
instanceof 判断 Record 类型
instanceof 能够与 Java Record 一起使用。编译器知道记录组件的确切数量和类型。 step1:声明 Person Record,拥有两个属性 name 和 age
代码语言:javascript复制public record Person(String name,Integer age) {
}
step2: 在一个业务方法判断当是 Record 类型时,继续判断 age 年龄是否满足 18 岁。
代码语言:javascript复制public class SomeService {
//定义业务方法,判断年龄是否18
public boolean isEligible(Object obj){
//判断 obj 为 Person 记录类型
if( obj instanceof Person(String name, Integer age)){
return age >= 18;
}
return false;
}
}
instanceof 还可以下面的方式
代码语言:javascript复制if( obj instanceof Person(String name, Integer age) person){
return person.age() >= 18;
}
或者
if( obj instanceof Person p){
return p.age() >= 18;
}
step3: 测试代码
代码语言:javascript复制@Test
public void test08() {
Person person = new Person("李四",20);
SomeService someService = new SomeService();
boolean flag = someService.isEligible(person);
System.out.println("flag = " flag);
}
控制台输出:
处理判断中 Record 为 null Java Record 能够自动处理 null。 step1:record 为 null
代码语言:javascript复制@Test
public void test09() {
SomeService someService = new SomeService();
boolean flag = someService.isEligible(null);
System.out.println("flag = " flag);
}
控制台输出 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 类有关的两个方法:
代码语言:javascript复制boolean isRecord() : 判断一个类是否是 Record 类型 RecordComponent[] getRecordComponents():Record 的数组,表示此记录类的所有记录组件
@Test
public void test10() {
Address address = new Address("大连","旅顺口区兴发路216号","10010");
PhoneNumber phoneNumber = new PhoneNumber("010","400-8080-105");
Customer customer = new Customer("C1001","李四",phoneNumber,address);
//判断其是否为java record类型
boolean record = customer.getClass().isRecord();
System.out.println("record = " record);
RecordComponent[] recordComponents = customer.getClass().getRecordComponents();
for(RecordComponent recordComponent :recordComponents){
System.out.println("recordComponent = " recordComponent);
}