使用不可变对象创建值对象

在回答我最近的文章中AutoValue:生成的不可变的值类 , 布兰登认为,这可能是有趣的,看看如何AutoValue比较项目Lombok和Immutables和凯文借调这一点。 我同意这是一个好主意,但是我首先将这篇文章发布为Immutables的简要概述,因为我已经为Lombok和AutoValue提供了类似的文章。

可从Maven中央存储库中获得Immutables 2.2.5 , 其许可证页面指出“ Imputables工具箱和所有必需的依赖项均在Apache软件许可2.0版中涵盖。” 开始吧! 网页指出“运行Immutables注释处理器需要Java 7或更高版本。”

像AutoValue这样的不可变对象使用编译时注释来生成定义不可变对象的类的源代码。 因为它们都使用这种方法,所以都只引入了编译时依赖性,并且在应用程序的运行时类路径上不需要它们各自的JAR。 换句话说,不可变的JAR必须位于编译器( javac )的类路径上,而不是位于Java启动器( java )的类路径上。

下一个代码清单( Person.java )中显示了“模板” Person类的代码清单。 它看起来与我在AutoValue演示中使用的Person.java非常相似。

人.java

package dustin.examples.immutables;import org.immutables.value.Value;/*** Represents an individual as part of demonstration of* the Immutables project (http://immutables.github.io/).*/
@Value.Immutable  // concrete extension will be generated by Immutables
abstract class Person
{/*** Provide Person's last name.** @return Last name of person.*/abstract String lastName();/*** Provide Person's first name.** @return First name of person.*/abstract String firstName();/*** Provide Person's birth year.** @return Person's birth year.*/abstract long birthYear();
}

我在AutoValue帖子中列出的“ template”类和“ template”类的唯一区别是包的名称,正在演示哪个产品的Javadoc注释以及(最重要的是)导入并应用于类。 在AutoValue示例中有一个特定的“创建”方法不在Immutables示例中,但这只是因为我没有演示AutoValue生成器的使用,这将使“ create”方法变得不必要。

当我在类路径上适当地指定使用Immutables并使用javac编译以上源代码时,将调用注释处理器并生成以下Java源代码:

ImmutablePerson.java

package dustin.examples.immutables;import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import javax.annotation.Generated;/*** Immutable implementation of {@link Person}.* <p>* Use the builder to create immutable instances:* {@code ImmutablePerson.builder()}.*/
@SuppressWarnings("all")
@Generated({"Immutables.generator", "Person"})
final class ImmutablePerson extends Person {private final String lastName;private final String firstName;private final long birthYear;private ImmutablePerson(String lastName, String firstName, long birthYear) {this.lastName = lastName;this.firstName = firstName;this.birthYear = birthYear;}/*** @return The value of the {@code lastName} attribute*/@OverrideString lastName() {return lastName;}/*** @return The value of the {@code firstName} attribute*/@OverrideString firstName() {return firstName;}/*** @return The value of the {@code birthYear} attribute*/@Overridelong birthYear() {return birthYear;}/*** Copy the current immutable object by setting a value for the {@link Person#lastName() lastName} attribute.* An equals check used to prevent copying of the same value by returning {@code this}.* @param lastName A new value for lastName* @return A modified copy of the {@code this} object*/public final ImmutablePerson withLastName(String lastName) {if (this.lastName.equals(lastName)) return this;String newValue = Objects.requireNonNull(lastName, "lastName");return new ImmutablePerson(newValue, this.firstName, this.birthYear);}/*** Copy the current immutable object by setting a value for the {@link Person#firstName() firstName} attribute.* An equals check used to prevent copying of the same value by returning {@code this}.* @param firstName A new value for firstName* @return A modified copy of the {@code this} object*/public final ImmutablePerson withFirstName(String firstName) {if (this.firstName.equals(firstName)) return this;String newValue = Objects.requireNonNull(firstName, "firstName");return new ImmutablePerson(this.lastName, newValue, this.birthYear);}/*** Copy the current immutable object by setting a value for the {@link Person#birthYear() birthYear} attribute.* A value equality check is used to prevent copying of the same value by returning {@code this}.* @param birthYear A new value for birthYear* @return A modified copy of the {@code this} object*/public final ImmutablePerson withBirthYear(long birthYear) {if (this.birthYear == birthYear) return this;return new ImmutablePerson(this.lastName, this.firstName, birthYear);}/*** This instance is equal to all instances of {@code ImmutablePerson} that have equal attribute values.* @return {@code true} if {@code this} is equal to {@code another} instance*/@Overridepublic boolean equals(Object another) {if (this == another) return true;return another instanceof ImmutablePerson&& equalTo((ImmutablePerson) another);}private boolean equalTo(ImmutablePerson another) {return lastName.equals(another.lastName)&& firstName.equals(another.firstName)&& birthYear == another.birthYear;}/*** Computes a hash code from attributes: {@code lastName}, {@code firstName}, {@code birthYear}.* @return hashCode value*/@Overridepublic int hashCode() {int h = 31;h = h * 17 + lastName.hashCode();h = h * 17 + firstName.hashCode();h = h * 17 + Long.hashCode(birthYear);return h;}/*** Prints the immutable value {@code Person} with attribute values.* @return A string representation of the value*/@Overridepublic String toString() {return "Person{"+ "lastName=" + lastName+ ", firstName=" + firstName+ ", birthYear=" + birthYear+ "}";}/*** Creates an immutable copy of a {@link Person} value.* Uses accessors to get values to initialize the new immutable instance.* If an instance is already immutable, it is returned as is.* @param instance The instance to copy* @return A copied immutable Person instance*/public static ImmutablePerson copyOf(Person instance) {if (instance instanceof ImmutablePerson) {return (ImmutablePerson) instance;}return ImmutablePerson.builder().from(instance).build();}/*** Creates a builder for {@link ImmutablePerson ImmutablePerson}.* @return A new ImmutablePerson builder*/public static ImmutablePerson.Builder builder() {return new ImmutablePerson.Builder();}/*** Builds instances of type {@link ImmutablePerson ImmutablePerson}.* Initialize attributes and then invoke the {@link #build()} method to create an* immutable instance.* <p><em>{@code Builder} is not thread-safe and generally should not be stored in a field or collection,* but instead used immediately to create instances.</em>*/static final class Builder {private static final long INIT_BIT_LAST_NAME = 0x1L;private static final long INIT_BIT_FIRST_NAME = 0x2L;private static final long INIT_BIT_BIRTH_YEAR = 0x4L;private long initBits = 0x7L;private String lastName;private String firstName;private long birthYear;private Builder() {}/*** Fill a builder with attribute values from the provided {@code Person} instance.* Regular attribute values will be replaced with those from the given instance.* Absent optional values will not replace present values.* @param instance The instance from which to copy values* @return {@code this} builder for use in a chained invocation*/public final Builder from(Person instance) {Objects.requireNonNull(instance, "instance");lastName(instance.lastName());firstName(instance.firstName());birthYear(instance.birthYear());return this;}/*** Initializes the value for the {@link Person#lastName() lastName} attribute.* @param lastName The value for lastName * @return {@code this} builder for use in a chained invocation*/public final Builder lastName(String lastName) {this.lastName = Objects.requireNonNull(lastName, "lastName");initBits &= ~INIT_BIT_LAST_NAME;return this;}/*** Initializes the value for the {@link Person#firstName() firstName} attribute.* @param firstName The value for firstName * @return {@code this} builder for use in a chained invocation*/public final Builder firstName(String firstName) {this.firstName = Objects.requireNonNull(firstName, "firstName");initBits &= ~INIT_BIT_FIRST_NAME;return this;}/*** Initializes the value for the {@link Person#birthYear() birthYear} attribute.* @param birthYear The value for birthYear * @return {@code this} builder for use in a chained invocation*/public final Builder birthYear(long birthYear) {this.birthYear = birthYear;initBits &= ~INIT_BIT_BIRTH_YEAR;return this;}/*** Builds a new {@link ImmutablePerson ImmutablePerson}.* @return An immutable instance of Person* @throws java.lang.IllegalStateException if any required attributes are missing*/public ImmutablePerson build() {if (initBits != 0) {throw new IllegalStateException(formatRequiredAttributesMessage());}return new ImmutablePerson(lastName, firstName, birthYear);}private String formatRequiredAttributesMessage() {List<String> attributes = new ArrayList<String>();if ((initBits & INIT_BIT_LAST_NAME) != 0) attributes.add("lastName");if ((initBits & INIT_BIT_FIRST_NAME) != 0) attributes.add("firstName");if ((initBits & INIT_BIT_BIRTH_YEAR) != 0) attributes.add("birthYear");return "Cannot build Person, some of required attributes are not set " + attributes;}}
}

通过检查生成的代码,可以得出一些结论(您会发现它们与我之前的文章中为AutoValue列出的观察结果非常相似):

  • 生成的类扩展(实现继承)了手写的抽象类,从而允许使用代码使用手写类的API,而不必知道正在使用生成的类。
  • 即使没有在源类中直接定义任何字段,也将生成字段; 不可变对象根据提供的abstract访问器方法解释字段。
  • 生成的类不为字段提供“设置” / mutator方法(get / accessor方法)。 这并不奇怪,因为值对象的关键概念是它们是不可变的,甚至这个项目的名称( Immutables )也暗示了这一特征。 请注意, Immutables确实为具有@ Value.Modifiable批注的可修改对象提供了一些功能。
  • 考虑到每个字段的类型,将自动为每个字段适当地生成equals(Object) , hashCode()和toString()的实现。
  • 在源类和方法上的Javadoc注释不会在生成的扩展类上重现。 相反,在生成的类的方法上提供了更简单(更通用)的Javadoc注释,在构建器类的方法上提供了更重要(但仍是通用的)的Javadoc注释。

正如我关于AutoValue所述,使用诸如Immutables生成之类的方法的主要优点之一是,开发人员可以专注于特定类应支持的更高级的概念,而代码生成则可以确保较低级别的细节。一致且正确地实施。 但是,使用这种方法时要记住一些事情。

  • 当开发人员受过足够的训练以检查和维护抽象的“源” Java类而不是生成的类时, 不可变对象最有帮助。
    • 下次注释处理再次生成该类时,对生成类的更改将被覆盖,否则必须停止该类的生成,以免发生这种情况。
  • 您将需要设置build / IDE,以便将生成的类视为“源代码”,以便抽象类可以编译,并且对生成的类的任何依赖项也可以编译。
  • 如果要将可变字段与不可变项一起使用,则必须特别小心(如果通常要选择使用不可变项或值对象,通常就是这种情况)。

结论

我的结论几乎与我在AutoValue上发表的文章一样。 Immutables允许开发人员编写更简洁的代码,重点放在高级细节上,并将繁琐的底层(通常是容易出错的)细节委派给Immutables ,以实现自动代码生成。 这类似于IDE的源代码生成可以执行的操作,但是Immutables相对于IDE方法的优势是Immutables可以在每次编译代码时重新生成源代码,从而使生成的代码保持最新。 Immutables的这一优势也是Java自定义注释处理功能强大的一个很好的例子。

翻译自: https://www.javacodegeeks.com/2016/06/creating-value-objects-immutables.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/353754.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

css3 transform实现水平和垂直居中

代码如下&#xff1a; <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><meta http-equiv"X-UA-Compatible…

ubuntu vnc 远程连接桌面

ubuntu vnc 远程连接桌面 2011-05-18 10:30:48 分类&#xff1a; LINUX 一、WindowsXP远程连接Ubuntu的桌面 1.在WindowsXP上下载并安装RealVNC; 2.在Ubuntu中操作&#xff0c;打开 应用程序/互联网 /远程桌面查看器; 3.首先要配置一下&#xff0c;打开 系统/首选项/远程桌面&…

hkws摄像头拆机

转载于:https://www.cnblogs.com/feipeng8848/p/8961924.html

java虚拟机常用命令工具

java虚拟机常用命令工具 博客分类&#xff1a; 虚拟机 虚拟机jvmjava 一、概述 程序运行中经常会遇到各种问题&#xff0c;定位问题时通常需要综合各种信息&#xff0c;如系统日志、堆dump文件、线程dump文件、GC日志等。通过虚拟机监控和诊断工具可以帮忙我们快速获取、分…

Spring Cloud Zuul –编写过滤器

Netflix OSS项目Zuul充当后端服务的网关&#xff0c;并支持添加安全性&#xff0c;路由等边缘功能。 在Zuul世界中&#xff0c;称为Zuul过滤器的组件提供了特定的边缘功能&#xff0c;为基于Spring Cloud的项目编写这种过滤器非常简单。 此处提供了添加过滤器的良好参考。 在这…

嵌入式基础篇 - 第2章 Systick系统定时器

2.1 STM32 的时钟系统 STM32 芯片为了实现低功耗&#xff0c;设计了一个功能完善但却非常复杂的时钟系统。普通的MCU 一般只要配置好 GPIO 的寄存器就可以使用了&#xff0c;但 STM32 还有一个步骤&#xff0c;就是开启外设时钟。 图2-1 STM32的时钟树在 STM32 中&#xff0c;…

kill所有java进程

kill所有java进程 ps -ef | grep java | grep -v grep |awk {print $2} | xargs -p kill -9如果不需要询问&#xff0c;把xargs后面 -p 参数去掉Aix 通过shell脚本kill杀指定进程&#xff0c;比如杀所有java进程 2012-11-16 15:31, Tags: 127人阅读----脚本杀进程-------------…

sqa计划

一、sqa计划 本计划是对“生活在长大”项目的流程规范和约定&#xff0c;本次计划包括质量保证、质量控制、数据收集和统计报告四部分。 质量保证&#xff1a; 保证项目的完整和运行&#xff0c;没有重大BUG。 计划进度 任务      时间        结果       …

正确地kill java历程

正确地kill java历程 www.MyException.Cn 发布于&#xff1a;2012-06-27 14:20:08 浏览&#xff1a;4次正确地kill java进程在linux/unix下&#xff0c;你会怎么中止一个java进程&#xff1f; 你可能会回答 kill -9 pid&#xff0c;这是一种在多数情况下正确的做法。不过&a…

并发加对象锁_通用并发对象池

并发加对象锁在本文中&#xff0c;我们将介绍如何在Java中创建对象池。 近年来&#xff0c;JVM的性能成倍增加&#xff0c;大多数类型的对象几乎都变得多余&#xff0c;从而提高了对象池的性能。 从本质上讲&#xff0c;对象的创建不再像以前那样昂贵。 但是&#xff0c;有些对…

SpringAOP02 自定义注解

1 自定义注解 1.1 创建自定义注解 从java5开始就可以利用 interface 来定义自定义注解 技巧01&#xff1a;注解不能直接干扰程序代码的运行&#xff08;即&#xff1a;注解的增加和删除操作后&#xff0c;代码都可以正常运行&#xff09; 技巧02&#xff1a;Retention 用来声明…

MFC处理回车窗口消失

MFC处理回车窗口消失 2011-04-24 12:21:31| 分类&#xff1a; C&C&VC | 标签&#xff1a;对话框 回车 mfc 消失 |字号大中小 订阅 我的方法是&#xff1a;找到自己设计的按钮&#xff0c;在属性-风格中选择default button&#xff1b;如果没有自己设计的but…

您的框架有多可扩展性?

在参加会议时&#xff0c;我们总是会见到高素质的决策者&#xff0c;他们经常问同样的问题&#xff1a; 您的框架有多可扩展性&#xff1f;如果我需要比您开箱即用的功能更多的东西怎么办&#xff1f; 。 这个问题是非常合理的&#xff0c;因为他们只是不想被卡在开发曲线的中间…

linux常用命令:touch 命令

linux的touch命令不常用&#xff0c;一般在使用make的时候可能会用到&#xff0c;用来修改文件时间戳&#xff0c;或者新建一个不存在的文件。 1&#xff0e;命令格式&#xff1a; touch [选项]... 文件... 2&#xff0e;命令参数&#xff1a; -a 或--timeatime或--timeacces…

用回车键实现MFC对话框中TAB键控件输入焦点在控件中跳转的效果(转)

用回车键实现MFC对话框中TAB键控件输入焦点在控件中跳转的效果&#xff08;转&#xff09; 版权声明&#xff1a;转载时请以超链接形式标明文章原始出处和作者信息及本声明 http://hcq11.blogbus.com/logs/54217707.html 近日在为一个数据应用写数据输入界面&#xff0c;大量…

python-面向对象编程设计与开发

编程范式 1、对不同类型的任务&#xff0c;所采取不同的解决问题的思路。 2、编程范式有两种 1、面向过程编程 2、面向对象编程 面向过程编程 什么是面向过程编程&#xff1f; 过程——解决问题的步骤 要解决一个大的问题 1、先把大问题拆分成若干小问题或子过程。 2、然后子过…

MFC下列表控件的使用

MFC下列表控件的使用 2012-11-09 16:46:57| 分类&#xff1a; 程序VC相关 | 标签&#xff1a; |字号大中小 订阅 1、应该加入头文件 #include <Atlbase.h>2、示例m_list.SetExtendedStyle(LVS_EX_GRIDLINES|LVS_EX_FULLROWSELECT|LVS_EX_ONECLICKACTIVATE);m_lis…

jbehave_使用JBehave,Gradle和Jenkins的行为驱动开发(BDD)

jbehave行为驱动开发 &#xff08;BDD&#xff09;是一个协作过程 &#xff0c;产品所有者&#xff0c;开发人员和测试人员可以合作交付可为业务带来价值的软件。 BDD是 测试驱动开发 &#xff08;TDD&#xff09; 的合理下一步 。 行为驱动的发展 本质上&#xff0c;BDD是一…

Confluence 6 考虑使用自定义 CSS

CSS 的知识储备 如果你没有有关 CSS 的相关知识&#xff0c;请参考页面 CSS Resources section 中的内容。当你打算开始对 Confluence 的样式表进行修改之前&#xff0c;你应该对 CSS 有一些相关的了解和知识储备。 安全 自定义 CSS 有可能被在页面中注入脚本&#xff0c;有跨…

MFC--CColorDialog的使用

MFC--CColorDialog的使用 2012-05-07 11:05:32| 分类&#xff1a; 学习mfc/c | 标签&#xff1a; |字号大中小 订阅 要在类中定义一个存储颜色的变量COLORREF m_color; 创建一个按钮&#xff0c;用来调用CColorDialog&#xff0c;用以改变静态文本的颜色&#xff0c;&a…