在上一篇有关Lombok库的文章中 ,我描述了一个库,该库有助于处理Java中的样板代码( 是的,我知道这些问题已经在Kotlin中解决了 ,但这是现实生活,我们不能一味地坐下来,一旦出现较新或更简单的语言,请重写每个现有项目)。 但是生活中有很多事情, Lombok项目有其他选择。 让我们也给他们一个机会。
本文的代码示例可以在此处和此处找到。
它实际上是Lombok的替代方案-因为您不能一次使用两者。 或者,至少事实证明,在同一个项目中同时使用IntelliJ IDEA和IntelliJ IDEA时,您将遇到很多困难,因为这是许多人真正选择的IDE,因为这两个库处理批注处理的方式不同。 因此,两个人都无法生存,而另一个人则得以幸存,这大约是哈利·波特和伏地魔的预言所表达的方式 。
因此,我们已经知道使用Lombok批注的Person类的外观 :
@Builder(toBuilder = true)
@ToString
@EqualsAndHashCode
@AllArgsConstructor(access = AccessLevel.PRIVATE)
public class Person {@NonNull@Getterprivate final String lastName;@NonNull@Getterprivate final String firstName;@NonNull@Getterprivate 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.Builderpublic 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();@Testpublic void testEquals() throws Exception {Person JOHN_COPY = Person.create(JOHN.lastName(), JOHN.firstName(), JOHN.age());assertThat(JOHN_COPY).isEqualTo(JOHN);}@Testpublic void testNotEquals() throws Exception {assertThat(JANE).isNotEqualTo(JOHN);}@Testpublic void testHashCode() throws Exception {Person JOHN_COPY = Person.create(JOHN.lastName(), JOHN.firstName(), JOHN.age());assertThat(JOHN_COPY.hashCode()).isEqualTo(JOHN.hashCode());}@Testpublic void testHashCodeNotEquals() throws Exception {Person JOHN_COPY = Person.create(JOHN.lastName(), JOHN.firstName(), JOHN.age());assertThat(JOHN_COPY.hashCode()).isNotEqualTo(JANE.hashCode());}@Testpublic 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();@Testpublic void testEquals() throws Exception {//ImmutablePerson JOHN_COPY = ImmutablePerson.builder().from(JOHN).build();Person JOHN_COPY = ImmutablePerson.copyOf(JOHN);assertThat(JOHN_COPY).isEqualTo(JOHN);}@Testpublic void testNotEquals() throws Exception {assertThat(JANE).isNotEqualTo(JOHN);}@Testpublic void testHashCode() throws Exception {Person JOHN_COPY = ImmutablePerson.copyOf(JOHN);assertThat(JOHN_COPY.hashCode()).isEqualTo(JOHN.hashCode());}@Testpublic void testHashCodeNotEquals() throws Exception {Person JOHN_COPY = ImmutablePerson.copyOf(JOHN);assertThat(JOHN_COPY.hashCode()).isNotEqualTo(JANE.hashCode());}@Testpublic 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语言的挑战之一就是冗长和样板代码。 但是有许多工具可以处理它,并且可以选择最合适的库,而不是通过复制粘贴或尝试编写自己的代码生成器进行编码。
好好利用它们。
好好用
翻译自: https://www.javacodegeeks.com/2018/03/lombok-autovalue-and-immutables-or-how-to-write-less-and-better-code-returns.html