Java 14 Records类

时间:2020-02-23 14:36:48  来源:igfitidea点击:

Java 14引入了一种创建类的新方法,称为Records。
在本教程中,我们将学习:

  • 为什么我们需要Java记录
  • 如何创建记录并使用它
  • 覆盖和扩展Records类

为什么我们需要Java记录?

Java的普遍抱怨之一是它的冗长。
如果必须创建一个简单的POJO类,则需要以下样板代码。

  • 私有字段
  • Getter和Setter方法
  • 构造函数
  • hashCode()、equals()和toString()方法。

这种冗长的语言是引起人们对Kotlin和龙目岛项目高度兴趣的原因之一。

实际上,每次编写这些通用方法的无奈之举导致在Java IDE(例如Eclipse和IntelliJ IDEA)中创建这些通用方法的捷径。

这是显示Eclipse IDE选项以生成类的仪式方法的屏幕快照。

Eclipse生成仪式方法的快捷方式

Java Records通过提供一个紧凑的结构来创建POJO类来消除这种冗长的含义。

如何创建Java记录

Java Records是一种预览功能,是在JEP 359下开发的。
因此,您需要两件事在Java项目中创建Records。

  • 已安装JDK 14。
    如果您使用的是IDE,那么它也必须提供对Java 14的支持。
    Eclipse和IntelliJ都已经提供了对Java 14的支持,因此我们在这里很好。

  • 启用预览功能:默认情况下,预览功能处于禁用状态。
    您可以通过Project Java Compiler设置在Eclipse中启用它。

Java 14在Eclipse中启用预览功能

您可以使用--enable-preview -source 14选项在命令行中启用Java 14预览功能。

假设我要创建一个Employee模型类。
它看起来像下面的代码。

package com.theitroad.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 + "]";
	}

}

ew,这是70余行自动生成的代码。
现在,让我们看看如何创建Employee Record类,该类基本上提供相同的功能。

package com.theitroad.java14;

import java.util.Map;

public record EmpRecord(int id, String name, long salary, Map<String, String> addresses) {
}

现在,让我们使用" javap"命令找出在编译记录时幕后发生的情况。

# 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();
}
# 

Java Record类的详细信息

如果需要更多内部详细信息,请运行带有-v选项的javap命令。

# javap -v EmpRecord 

关于记录类的要点

  • Record类是最终的,因此我们无法扩展它。

  • Record类隐式扩展了java.lang.Record类。

  • 记录声明中指定的所有字段均为最终字段。

  • 记录字段是"浅"不变的,并取决于类型。
    例如,我们可以通过访问地址字段然后对其进行更新来更改地址字段。

  • 使用记录定义中指定的所有字段创建单个构造函数。

  • Record类自动为字段提供访问器方法。
    方法名称与字段名称相同,与通用和常规getter方法不同。

  • Record类也提供hashCode(),equals()和toString()实现。

在Java程序中使用记录

让我们看一个使用EmpRecord类的简单示例。

package com.theitroad.java14;

public class RecordTest {

	public static void main(String[] args) {
		
		EmpRecord empRecord1 = new EmpRecord(10, "hyman", 10000, null);
		EmpRecord empRecord2 = new EmpRecord(10, "hyman", 10000, null);

		//toString()
		System.out.println(empRecord1);
		
		//accessing fields
		System.out.println("Name: "+empRecord1.name()); 
		System.out.println("ID: "+empRecord1.id());
		
		//equals()
		System.out.println(empRecord1.equals(empRecord2));
		
		//hashCode()
		System.out.println(empRecord1 == empRecord2);		
	}
}

输出:

EmpRecord[id=10, name=hyman, salary=10000, addresses=null]
Name: hyman
ID: 10
true
false

Record对象的工作方式与任何模型类,数据对象等相同。

扩展记录构造器

有时,我们想进行一些验证或者登录构造函数。
例如,员工ID和薪水不应为负数。
默认构造函数将没有此验证。
我们可以在记录类中创建一个紧凑的构造函数。
该构造函数的代码将放置在自动生成的构造函数的开头。

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:

EmpRecord empRecord1 = new EmpRecord(-10, "hyman", 10000, null);

我们将获得运行时异常为:

Exception in thread "main" java.lang.IllegalArgumentException: employee id can't be negative
	at com.theitroad.java14.EmpRecord.<init>(EmpRecord.java:9)

记录类可以有方法吗?

是的,我们可以在记录中创建方法。

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;
	}
}

但是,记录本来是数据载体。
我们应该避免在记录类中使用实用程序方法。
例如,可以在实用程序类中创建上述方法。