跳转至

Java Records:简洁的数据载体新方式

简介

在Java 14中引入的Records,为开发者提供了一种更为简洁、紧凑的方式来创建不可变的数据载体类。传统的JavaBean需要大量样板代码来定义字段、构造函数、访问器、修改器以及 equals()hashCode()toString() 方法。Records则大大简化了这一过程,让代码更加简洁易读,专注于数据本身。

目录

  1. Records基础概念
  2. Records使用方法
    • 定义Record
    • 访问Record字段
    • Record构造函数
  3. Records常见实践
    • 作为方法参数和返回值
    • 与集合框架结合使用
  4. Records最佳实践
    • 保持Record简单
    • 不可变设计原则
    • 与其他Java特性结合
  5. 小结
  6. 参考资料

Records基础概念

Record是一种特殊的类,它是不可变的,并且主要用于保存数据。它自动为其字段生成构造函数、访问器、equals()hashCode()toString() 方法。Record类不能被继承,也不能声明构造函数以外的方法,除非这些方法是从 Object 类重写的,或者是默认方法或静态方法。

Records使用方法

定义Record

定义一个Record非常简单,使用 record 关键字,后跟类名和一对括号,括号内定义字段。例如,定义一个表示二维坐标点的Record:

public record Point(int x, int y) {}

上述代码定义了一个名为 Point 的Record,它有两个字段 xy,类型都是 int。编译器会自动为 Point 生成以下内容: - 一个包含所有字段的构造函数 - 每个字段的访问器方法(例如 x()y()) - equals()hashCode()toString() 方法的实现

访问Record字段

可以通过生成的访问器方法来访问Record的字段:

public class Main {
    public static void main(String[] args) {
        Point point = new Point(5, 10);
        int x = point.x();
        int y = point.y();
        System.out.println("x: " + x + ", y: " + y);
    }
}

在上述代码中,point.x()point.y() 分别返回 Point 对象的 xy 字段值。

Record构造函数

虽然编译器会自动生成包含所有字段的构造函数,但也可以自定义构造函数来进行额外的初始化或验证:

public record Point(int x, int y) {
    public Point {
        if (x < 0 || y < 0) {
            throw new IllegalArgumentException("Coordinates cannot be negative");
        }
    }
}

上述代码中,自定义的构造函数(没有参数列表)用于验证坐标值是否为负数。如果是,则抛出 IllegalArgumentException

Records常见实践

作为方法参数和返回值

Records非常适合作为方法的参数和返回值,因为它们简洁地封装了数据。例如:

public class Geometry {
    public static double distance(Point p1, Point p2) {
        int dx = p2.x() - p1.x();
        int dy = p2.y() - p1.y();
        return Math.sqrt(dx * dx + dy * dy);
    }

    public static Point midpoint(Point p1, Point p2) {
        int midX = (p1.x() + p2.x()) / 2;
        int midY = (p1.y() + p2.y()) / 2;
        return new Point(midX, midY);
    }
}

在上述代码中,distance 方法接受两个 Point 对象作为参数,并返回它们之间的距离;midpoint 方法接受两个 Point 对象,计算并返回它们的中点。

与集合框架结合使用

Records可以很好地与Java集合框架集成。例如,将 Point 对象存储在 List 中:

import java.util.ArrayList;
import java.util.List;

public class Main {
    public static void main(String[] args) {
        List<Point> points = new ArrayList<>();
        points.add(new Point(1, 2));
        points.add(new Point(3, 4));

        for (Point point : points) {
            System.out.println(point);
        }
    }
}

上述代码创建了一个 Point 对象的列表,并遍历打印每个 Point 对象。由于Record自动生成了 toString() 方法,打印输出非常直观。

Records最佳实践

保持Record简单

Record的设计初衷是作为简单的数据载体,因此应尽量保持其简单性。避免在Record中添加复杂的业务逻辑,将业务逻辑放在专门的服务类中。

不可变设计原则

Record本身是不可变的,这符合不可变设计原则。不可变对象在多线程环境中更加安全,并且易于理解和维护。在使用Record时,确保所有字段都是不可变类型,以保持整体的不可变性。

与其他Java特性结合

可以将Records与其他Java特性如Stream API、Lambda表达式等结合使用,以实现更强大的功能。例如,使用Stream API对 Point 列表进行过滤和映射:

import java.util.List;
import java.util.stream.Collectors;

public class Main {
    public static void main(String[] args) {
        List<Point> points = List.of(new Point(1, 2), new Point(3, 4), new Point(5, 6));

        List<Integer> xValues = points.stream()
              .map(Point::x)
              .collect(Collectors.toList());

        List<Point> filteredPoints = points.stream()
              .filter(point -> point.y() > 3)
              .collect(Collectors.toList());

        System.out.println("x values: " + xValues);
        System.out.println("Filtered points: " + filteredPoints);
    }
}

上述代码展示了如何使用Stream API对 Point 列表进行操作,获取所有点的 x 坐标值,并过滤出 y 坐标大于3的点。

小结

Java Records为开发者提供了一种简洁、高效的方式来创建不可变的数据载体类。通过自动生成构造函数、访问器、equals()hashCode()toString() 方法,减少了大量样板代码,提高了代码的可读性和可维护性。在实际开发中,合理运用Records可以提升开发效率,并遵循最佳实践来确保代码的质量和性能。

参考资料

希望通过本文,读者能深入理解并高效使用Java Records。