Java 14引入了一种称为Records的新类创建方式。在本教程中,我们将学习:
- 为什么我们需要Java Records
- 如何创建Records并使用它
- 覆盖和扩展Records类
推荐阅读:Java 14特性
为什么我们需要Java Records?
Java一直以来的一个普遍批评是其冗长。如果您必须创建一个简单的POJO类,则需要以下样板代码。
- 私有字段
- Getter和Setter方法
- 构造函数
- hashCode()、equals()和toString()方法。
这种冗长是人们对Kotlin和Project Lombok兴趣高涨的原因之一。
事实上,编写这些通用方法的纯粹沮丧每次都会导致在Java IDE(如Eclipse和IntelliJ IDEA)中创建它们的快捷方式。
以下是显示Eclipse IDE 选项以为类生成典礼性方法的截图。

Java Records旨在通过提供一种紧凑的结构来创建POJO类以消除这种冗长。
如何创建Java Records
Java Records是一个预览功能,是在JEP 359下开发的。因此,在Java项目中创建Records需要两样东西。
- 已安装JDK 14。如果你使用的是IDE,它必须支持Java 14。Eclipse和IntelliJ都已经提供了对Java 14的支持,所以我们在这方面没问题。
- 启用预览功能:默认情况下,预览功能是禁用的。你可以在Eclipse的项目Java编译器设置中启用它。

你可以在命令行中使用--enable-preview -source 14
选项启用Java 14的预览功能。
假设我想创建一个Employee模型类。它将看起来像以下代码。
package com.journaldev.java14;
import java.util.Map;
public class Employee {
private int id;
private String name;
private long salary;
private Map<String, String> addresses;
public Employee(int id, String name, long salary, Map<String, String> addresses) {
super();
this.id = id;
this.name = name;
this.salary = salary;
this.addresses = addresses;
}
public int getId() {
return id;
}
public String getName() {
return name;
}
public long getSalary() {
return salary;
}
public Map<String, String> getAddresses() {
return addresses;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((addresses == null) ? 0 : addresses.hashCode());
result = prime * result + id;
result = prime * result + ((name == null) ? 0 : name.hashCode());
result = prime * result + (int) (salary ^ (salary >>> 32));
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Employee other = (Employee) obj;
if (addresses == null) {
if (other.addresses != null)
return false;
} else if (!addresses.equals(other.addresses))
return false;
if (id != other.id)
return false;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
if (salary != other.salary)
return false;
return true;
}
@Override
public String toString() {
return "Employee [id=" + id + ", name=" + name + ", salary=" + salary + ", addresses=" + addresses + "]";
}
}
哇,这是70多行自动生成的代码。现在让我们看看如何创建一个Employee Record类,它基本上提供了相同的功能。
package com.journaldev.java14;
import java.util.Map;
public record EmpRecord(int id, String name, long salary, Map<String, String> addresses) {
}
哇,这不能再更短了。我已经喜欢上了Record类。
现在,让我们使用javap
命令找出在编译Record时发生了什么。
# javac --enable-preview -source 14 EmpRecord.java
Note: EmpRecord.java uses preview language features.
Note: Recompile with -Xlint:preview for details.
# javap EmpRecord
Compiled from "EmpRecord.java"
public final class EmpRecord extends java.lang.Record {
public EmpRecord(int, java.lang.String, long, java.util.Map<java.lang.String, java.lang.String>);
public java.lang.String toString();
public final int hashCode();
public final boolean equals(java.lang.Object);
public int id();
public java.lang.String name();
public long salary();
public java.util.Map<java.lang.String, java.lang.String> addresses();
}
#

# javac —enable–preview –source 14 EmpRecord.java
# javap EmpRecord
如果您想获取更多内部细节,请使用 javap 命令并带有 -v 选项运行。
- A Record class is final, so we can’t extend it.
-
# javap -v EmpRecord
- Record 类的重要要点
- Record 类隐式扩展了
java.lang.Record
类。 - A single constructor is created with all the fields specified in the record definition.
- 在记录声明中指定的所有字段都是 final 的。
- 记录字段是“浅层”不可变的,并取决于类型。例如,我们可以通过访问地址字段然后对其进行更新来更改它。
Record 类自动为字段提供访问器方法。方法名与字段名相同,不像通用和传统的 getter 方法。
Record 类还提供了 hashCode()、equals() 和 toString() 的实现。
package com.journaldev.java14;
public class RecordTest {
public static void main(String[] args) {
EmpRecord empRecord1 = new EmpRecord(10, "Pankaj", 10000, null);
EmpRecord empRecord2 = new EmpRecord(10, "Pankaj", 10000, null);
在Java程序中使用记录
System.out.println(empRecord1);
让我们来看一个使用我们的EmpRecord类的简单示例。
System.out.println("Name: "+empRecord1.name());
System.out.println("ID: "+empRecord1.id());
// toString()
System.out.println(empRecord1.equals(empRecord2));
// 访问字段
System.out.println(empRecord1 == empRecord2);
}
}
// equals()
EmpRecord[id=10, name=Pankaj, salary=10000, addresses=null]
Name: Pankaj
ID: 10
true
false
// hashCode()
输出:
Record对象的工作方式与任何模型类、数据对象等相同。
public record EmpRecord(int id, String name, long salary, Map<String, String> addresses) {
public EmpRecord {
if (id < 0)
throw new IllegalArgumentException("employee id can't be negative");
if (salary < 0)
throw new IllegalArgumentException("employee salary can't be negative");
}
}
EmpRecord empRecord1 = new EmpRecord(-10, "Pankaj", 10000, null);
有时,我们希望在我们的构造函数中进行一些验证或记录。例如,员工ID和薪水不应该为负数。默认构造函数不会进行这种验证。我们可以在记录类中创建一个紧凑的构造函数。此构造函数的代码将放置在自动生成的构造函数的开头。
Exception in thread "main" java.lang.IllegalArgumentException: employee id can't be negative
at com.journaldev.java14.EmpRecord.<init>(EmpRecord.java:9)
如果我们像以下代码一样创建EmpRecord:
我们将会得到运行时异常:
public record EmpRecord(int id, String name, long salary, Map<String, String> addresses) {
public int getAddressCount() {
if (this.addresses != null)
return this.addresses().size();
else
return 0;
}
}
可以,在记录类中我们是可以创建方法的。
但是,记录类主要用于承载数据。我们应该避免在记录类中添加实用方法。例如,上面的方法可以创建在一个实用类中。
如果你认为在你的记录类中添加方法是必须的,请仔细思考你是否真的需要一个记录类?
Source:
https://www.digitalocean.com/community/tutorials/java-records-class