Immutables 简介
一、简介
在本文中,我们将展示如何使用 Immutables 库。
该库由注释和注释处理器组成,用于生成和使用可序列化和可定制的不可变对象。
2.Maven依赖
为了在项目中使用 Immutables,您需要将以下依赖项添加到 pom.xml 文件的 dependencies 部分:
<dependency>
<groupId>org.immutables</groupId>
<artifactId>value</artifactId>
<version>2.2.10</version>
<scope>provided</scope>
</dependency>
由于运行时不需要此工件,因此建议指定提供范围。
可以在此处找到该库的最新版本。
3. Immutables
该库从抽象类型生成不可变对象:接口、类、注释。
实现这一点的关键是正确使用 @Value.Immutable 注解。 它生成带注释类型的不可变版本,并在其名称前加上 Immutable 关键字。
如果我们尝试生成名为“X”的类的不可变版本,它将生成一个名为“ImmutableX”的类。生成的类不是递归不变的,因此最好记住这一点。
快速说明一下 - 因为 Immutables 使用注释处理,所以您需要记住在 IDE 中启用注释处理。
3.1.将@Value.Immutable与抽象类和接口结合使用
让我们创建一个简单的abstract类Person,由两个abstract访问器方法组成,代表要生成的字段,然后用@Value.Immutable 注释:
@Value.Immutable
public abstract class Person {
abstract String getName();
abstract Integer getAge();
}
注解处理完成后,我们可以在 target/generated-sources 目录中找到一个新生成的 ImmutablePerson 类 :
@Generated({"Immutables.generator", "Person"})
public final class ImmutablePerson extends Person {
private final String name;
private final Integer age;
private ImmutablePerson(String name, Integer age) {
this.name = name;
this.age = age;
}
@Override
String getName() {
return name;
}
@Override
Integer getAge() {
return age;
}
// toString, hashcode, equals, copyOf and Builder omitted
}
生成的类附带已实现的 toString、hashcode、equals 方法和 Stepbuilder ImmutablePerson.Builder。请注意,生成的构造函数具有私有访问权限。
为了构造 ImmutablePerson 类的实例,我们需要使用构建器或静态方法 ImmutablePerson.copyOf,它可以从Person 对象创建一个 ImmutablePerson 副本。
如果我们想使用构建器构造一个实例,我们可以简单地编写代码:
ImmutablePerson john = ImmutablePerson.builder()
.age(42)
.name("John")
.build();
生成的类是不可变的,这意味着它们不能被修改。如果要修改已存在的对象,可以使用“withX”方法之一,该方法不会修改原始对象,而是使用修改后的字段创建一个新实例。
让我们更新john的年龄并创建一个新的john43对象:
ImmutablePerson john43 = john.withAge(43);
在这种情况下,以下断言将成立:
assertThat(john).isNotSameAs(john43);
assertThat(john.getAge()).isEqualTo(42);
4. 附加实用程序
如果不能定制的话,这样的类生成就不会很有用。 Immutables 库附带了一组附加注释,可用于自定义 @Value.Immutable 的输出。要查看所有这些内容,请参阅 Immutables 的文档。
4.1. @Value.Parameter 注解
@Value.Parameter注解可用于指定应生成构造方法的字段。
如果你像这样注释你的类:
@Value.Immutable
public abstract class Person {
@Value.Parameter
abstract String getName();
@Value.Parameter
abstract Integer getAge();
}
可以通过以下方式实例化它:
ImmutablePerson.of("John", 42);
4.2. @Value.Default 注解
@Value.Default 注释允许您指定在未提供初始值时应使用的默认值。为此,您需要创建一个返回固定值的非抽象访问器方法,并使用 @Value.Default 对其进行注释:
@Value.Immutable
public abstract class Person {
abstract String getName();
@Value.Default
Integer getAge() {
return 42;
}
}
以下断言将成立:
ImmutablePerson john = ImmutablePerson.builder()
.name("John")
.build();
assertThat(john.getAge()).isEqualTo(42);
4.3. @Value.Auxiliary 注解
@Value.Auxiliary 注释可用于注释将存储在对象实例中的属性,但会被 equals、hashCode 和 toString 忽略该属性。
如果你像这样注释你的类:
@Value.Immutable
public abstract class Person {
abstract String getName();
abstract Integer getAge();
@Value.Auxiliary
abstract String getAuxiliaryField();
}
使用辅助字段时,以下断言为真:
ImmutablePerson john1 = ImmutablePerson.builder()
.name("John")
.age(42)
.auxiliaryField("Value1")
.build();
ImmutablePerson john2 = ImmutablePerson.builder()
.name("John")
.age(42)
.auxiliaryField("Value2")
.build();
assertThat(john1.equals(john2)).isTrue();
assertThat(john1.toString()).isEqualTo(john2.toString());
assertThat(john1.hashCode()).isEqualTo(john2.hashCode());
4.4. @Value.Immutable(Prehash = True) 注解
由于我们生成的类是不可变的并且永远无法修改,因此 hashCode 结果将始终保持不变,并且在对象实例化期间只能计算一次。
如果你像这样注释你的类:
@Value.Immutable(prehash = true)
public abstract class Person {
abstract String getName();
abstract Integer getAge();
}
检查生成的类时,您可以看到 hashcode 值现在已预先计算并存储在字段中:
@Generated({"Immutables.generator", "Person"})
public final class ImmutablePerson extends Person {
private final String name;
private final Integer age;
private final int hashCode;
private ImmutablePerson(String name, Integer age) {
this.name = name;
this.age = age;
this.hashCode = computeHashCode();
}
// generated methods
@Override
public int hashCode() {
return hashCode;
}
}
hashCode() 方法返回构造对象时生成的预先计算的哈希码。
5. 结论
在本快速教程中,我们展示了 Immutables 库的基本工作原理。
文章中的所有源代码和单元测试都可以在 GitHub 代码库 中找到。