JDK有用的新特性-Java Record

2023-10-16 15:51:35 浏览数 (2)

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 类有关的两个方法:

boolean isRecord() : 判断一个类是否是 Record 类型 RecordComponent[] getRecordComponents():Record 的数组,表示此记录类的所有记录组件

代码语言:javascript复制
@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);
    }

0 人点赞