lombok插件安装
在上一篇有关Lombok库的文章中,我描述了一个库,该库有助于处理Java中的样板代码( 是的,我知道这些问题已经在Kotlin中解决了,但这是现实生活,我们不能一味地坐下来,一旦出现较新或更简单的语言,请重写每个现有项目)。 但是生活中有许多事情,Lombok项目有其替代方案。 让我们也给他们一个机会。
它实际上是Lombok的替代方案-因为您不能一次使用两者。 或者,至少事实证明,在同一个项目中同时使用IntelliJ IDEA和IntelliJ IDEA时,您会遇到很多困难,因为这是许多人真正选择的IDE,因为这两个库处理批注处理的方式不同。 因此,两个人都无法生存,而另一个人则得以幸存,这大约是哈利·波特和伏地魔的预言听起来的样子。
因此,我们已经知道Person类带有Lombok批注的外观:
@Builder(toBuilder = true)
@ToString
@EqualsAndHashCode
@AllArgsConstructor(access = AccessLevel.PRIVATE)
public class Person {
@NonNull
@Getter
private final String lastName;
@NonNull
@Getter
private final String firstName;
@NonNull
@Getter
private final Integer age;
}
如果我们创建一个新项目并按此处所述使用autovalue ,则可以使用AutoValue Builders模仿几乎相同的模型。
现在让我们看看AutoValue模型的外观:
package autovalue.model;
import com.google.auto.value.AutoValue;
@AutoValue
public abstract class Person {
public abstract String lastName();
public abstract String firstName();
public abstract Integer age();
public static Person create(String lastName, String firstName, Integer age) {
return builder().lastName(lastName).firstName(firstName).age(age).build();
}
public static Builder builder() {
return new AutoValue_Person.Builder();
}
@AutoValue.Builder
public abstract static class Builder {
public abstract Builder lastName(String lastName);
public abstract Builder firstName(String firstName);
public abstract Builder age(Integer age);
public abstract Person build();
}
}
您可以看到的是,肯定有更多的代码。
Lombok生成带有单个注释的构建器时, AutoValue将使您创建自己的构建器代码-尽管不是全部。 基本上,您定义接口后,实现将由AutoValue生成的代码完成,您不必实际实现getter和setter中的代码。 即使我们同意AutoValue getter接口不会比Lombok字段定义花费更多的时间或空间,但是对于某些人来说,编写AutoValue构建器代码仍然是一件麻烦事。
但是,它可以提供更大的灵活性,因为您实际上可以更改构建器方法名称。 此外,代码分析和使用情况搜索是一个巨大的胜利–这样,您实际上可以分别查找实际的getter和setter的用法,这对开发人员也可能很重要。
实例的创建方法与Lombok相同。
final Person anna = Person.builder()
.age(31)
.firstName("Anna")
.lastName("Smith")
.build();
我们所有的测试都在代码更改最少的情况下运行,主要是因为AutoValue无法将实例转换为构建器(或者至少我无法轻易找到它),因此复制只是调用静态工厂方法:
package autovalue.model;
import org.junit.Test;
import static org.assertj.core.api.Java6Assertions.assertThat;
public class PersonTest {
private static Person JOHN = Person.builder()
.firstName("John")
.lastName("Doe")
.age(30)
.build();
private static Person JANE = Person.builder()
.firstName("Jane")
.lastName("Doe")
.age(30)
.build();
@Test
public void testEquals() throws Exception {
Person JOHN_COPY = Person.create(JOHN.lastName(), JOHN.firstName(), JOHN.age());
assertThat(JOHN_COPY).isEqualTo(JOHN);
}
@Test
public void testNotEquals() throws Exception {
assertThat(JANE).isNotEqualTo(JOHN);
}
@Test
public void testHashCode() throws Exception {
Person JOHN_COPY = Person.create(JOHN.lastName(), JOHN.firstName(), JOHN.age());
assertThat(JOHN_COPY.hashCode()).isEqualTo(JOHN.hashCode());
}
@Test
public void testHashCodeNotEquals() throws Exception {
Person JOHN_COPY = Person.create(JOHN.lastName(), JOHN.firstName(), JOHN.age());
assertThat(JOHN_COPY.hashCode()).isNotEqualTo(JANE.hashCode());
}
@Test
public void testToString() throws Exception {
String jane = JANE.toString();
assertThat(jane).contains(JANE.lastName());
assertThat(jane).contains(JANE.firstName());
assertThat(jane).contains("" + JANE.age());
assertThat(jane).doesNotContain(JOHN.firstName());
}
}
其他显而易见的区别:
- 您编写的AutoValue类始终是抽象的。 它们在AutoValue生成的代码中实现。
- AutoValue类是自动不可变的。 有一种解决方法,使它们具有不可变类型的属性。 即使您明确希望在实例上使用setter, 也不能。
为什么要使用AutoValue ? AutoValue的创建者会谨慎地在此处描述该库的收益,甚至就此创建一个完整的演示文稿。
该库还使用Java注释处理器来生成简单,安全和一致的值对象。 好吧,与前两个相同。 还有什么是新的? 让我们来看看。
最简单的值类如下所示。
package immutables.model;
import org.immutables.value.Value;
@Value.Immutable
public abstract class Person {
public abstract String lastName();
public abstract String firstName();
public abstract Integer age();
}
因此,具有抽象类的相同原理仅在生成的代码中实现。 为此,您需要启用IDE注释处理器,就像对Lombok一样(但对于AutoValue则不需要,因为它是由gradle插件完成的)。
那么,对象创建的外观如何?
final Person anna = ImmutablePerson.builder()
.age(31)
.firstName("Anna")
.lastName("Smith")
.build();
System.out.println(anna);
乍一看,最明显的区别是:
- 我们不声明构建器方法。
- 静态builder / factory方法不是在我们自己的类上创建的,而是在生成的类上创建的。
- 与AutoValue一样,无法在类上生成生成器,只能在生成器上生成。
- 生成的类也自动-ers,就是实例方法,允许通过改变一个属性来创建实例的副本补充说:
final ImmutablePerson anna = ImmutablePerson.builder()
.age(31)
.firstName("Anna")
.lastName("Smith")
.build();
System.out.println(anna);
final ImmutablePerson annaTheSecond = anna.withAge(23).withLastName("Smurf");
System.out.println(annaTheSecond);
- 该构建器具有自动添加的from()方法,该方法允许创建实例的精确副本,并且在生成的类上还有一个生成的静态copyOf()方法:
Person JOHN_COPY = ImmutablePerson.builder().from(JOHN).build();
// OR
Person JOHN_COPY = ImmutablePerson.copyOf(JOHN);
同样,我们的测试运行时所做的更改很小,主要是关于如何复制实例的:
package immutables.model;
import org.junit.Test;
import static org.assertj.core.api.Assertions.assertThat;
public class PersonTest {
private static Person JOHN = ImmutablePerson.builder()
.firstName("John")
.lastName("Doe")
.age(30)
.build();
private static Person JANE = ImmutablePerson.builder()
.firstName("Jane")
.lastName("Doe")
.age(30)
.build();
@Test
public void testEquals() throws Exception {
//ImmutablePerson JOHN_COPY = ImmutablePerson.builder().from(JOHN).build();
Person JOHN_COPY = ImmutablePerson.copyOf(JOHN);
assertThat(JOHN_COPY).isEqualTo(JOHN);
}
@Test
public void testNotEquals() throws Exception {
assertThat(JANE).isNotEqualTo(JOHN);
}
@Test
public void testHashCode() throws Exception {
Person JOHN_COPY = ImmutablePerson.copyOf(JOHN);
assertThat(JOHN_COPY.hashCode()).isEqualTo(JOHN.hashCode());
}
@Test
public void testHashCodeNotEquals() throws Exception {
Person JOHN_COPY = ImmutablePerson.copyOf(JOHN);
assertThat(JOHN_COPY.hashCode()).isNotEqualTo(JANE.hashCode());
}
@Test
public void testToString() throws Exception {
String jane = JANE.toString();
assertThat(jane).contains(JANE.firstName());
assertThat(jane).contains(JANE.lastName());
assertThat(jane).contains("" + JANE.age());
assertThat(jane).doesNotContain(JOHN.firstName());
}
}
关于Immutables库,还有很多要说的,因此这里有一个相当大的手册。 在本文中,我们仅对表面进行了一些刮擦。 例如,有关使用Immitables和样式自定义(方法前缀,构建器名称等)以及甚至为Mongo生成存储库以便将文档视为不可变对象的JSON序列化的更多详细信息。 但是,这比我在这篇简单文章中所涉及的要多得多。
要解决的问题是,尚未普及的Java语言的挑战之一就是冗长和样板代码。 但是有许多工具可以处理它,并且可以选择最合适的库,而不是通过复制粘贴或尝试编写自己的代码生成器进行编码。
好好利用它们。
好好用
lombok插件安装