详解Java的record关键字

1 分钟阅读

record关键字

record关键字主要提供了更简洁、更紧凑的final修饰的不可变数据类的定义方式。可以用于数据传输,并发编程等领域。

该特性最早于Java 14引入开始孵化,经历两次孵化,最终于Java 16转为正式特性。

使用方法及示例

在Java日常开发中,当我们需要定义个不可变数据类对象用于数据传输,并发编程等途径时。通常写法如下代码块所示.

以下示例代码块为了突显class和record的getter方法命名规则的差异,故意修改等效于record的x()和y()方法. 常规的getter方法命名方式应该为getX()和getY()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
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;
    }

    @Override
    public String toString() {
        return "Point{" +
                "x=" + x +
                ", y=" + y +
                '}';
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Point point = (Point) o;
        return x == point.x && y == point.y;
    }

    @Override
    public int hashCode() {
        return Objects.hash(x, y);
    }
}

使用record实现上面等同效果仅需简单的一行即可。

1
2
public record PointR(int x, int y) {
}

通过简单的对比,我们可以得知,record相当于节省了以下几个方面的代码。

  1. 全参数的构造函数
  2. getter方法
  3. hashCode和equals方法
  4. toString方法
  5. final修饰的类

定义静态属性字段,静态方法、类方法和属性字段注解

record内部 不允许 定义非静态属性字段. 除此之外,和class的用法基本上没有区别。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public record PointR(@Nullable int x, int y) {

    /**
     * 1. 定义静态属性字段
     */
    private static int z = 1;

    /**
     * 2. 定义静态方法
     */
    public static int distance(PointR o, PointR o1) {
        return 0;
    }

    /**
     * 3. 定义方法
     */
    public int distance(PointR o) {
        return 0;
    }
}

实现接口

record关键字隐式继承Record类,所以 不允许 在使用 extends 继承其他类。和class一样是单根继承,多接口实现。

1
2
3
4
5
6
7
8
//  扩展实现其他接口
public record PointR(int x, int y) implements Comparable<PointR> {

    @Override
    public int compareTo(PointR o) {
        return 0;
    }
}

泛型

1
2
3
4
5
6
7
8
//  Record使用泛型
public record PointR<R extends Serializable>(int x, int y, R r) {

    public static void main(String[] args) {
        //  测试
        PointR<ArrayList<Integer>> pointR = new PointR<>(1, 2, new ArrayList<Integer>());
    }
}

浅拷贝

record和class一样,实现Cloneable接口后,针对引用类型都是默认浅拷贝. 在并发环境时,需要考虑是否会受到浅拷贝影响。

1
2
3
4
5
6
7
8
public record PointR(int x, int y) implements Cloneable {

    @Override
    protected Object clone() throws CloneNotSupportedException {
        //  并发环境时,需要考虑是否会受到浅拷贝影响
        return super.clone();
    }
}

反射

相比于class的反射获取属性字段信息方式, record的反射有了颠覆性的变动,我个人感觉是更加简单和直观。

可以通过Class定义的geRecordComponents()方法获取record字段的record组件数组,顺序等同于record构造函数中定义的顺序。

1
RecordComponent[] recordComponents = PointR.class.getRecordComponents();

获取字段类型

Class对象中新增 isRecord() 方法用于区分类是否是record类.

1
PointR.class.isRecord()

其他常用的反射相关的方法基本上类似于Field。例如:RecordComponent#getType() 反射获取属性字段的类型, RecordComponent#getAnnotation() 反射获取属性字段的注解

1
2
3
4
5
6
RecordComponent[] recordComponents = PointR.class.getRecordComponents();
for (RecordComponent rc : recordComponents) {
    Assertions.assertEquals(int.class, rc.getType());
    rc.getAnnotation(Nullable.class)
}

知识共享许可协议

本作品采用 知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议 进行许可。欢迎转载、使用、重新发布,但务必保留文章署名 TinyZ Zzh (包含链接: https://tinyzzh.github.io ),不得用于商业目的,基于本文修改后的作品务必以相同的许可发布。 如有任何疑问,请 与我联系 (tinyzzh815@gmail.com)

TinyZ Zzh

TinyZ Zzh

专注于高并发服务器、网络游戏相关(Java、PHP、Unity3D、Unreal Engine等)技术,热爱游戏事业, 正在努力实现自我价值当中。

评论

  点击开始评论...