AutoValue —— Generated immutable value classes
本文参考
在《Effective Java》第三版第十条"Obey the general contract when overriding equals"中提到google的AutoValue框架能够自动生成equals()方法,实际上这个框架的作用不仅仅限于生成equals()方法那么简单,它还能够使值类通过静态工厂方法构建实例,并实现Builder构建者模式,省去了程序员对值类的重复性工作
github地址:https://github.com/google/auto/blob/master/value/userguide/index.md
环境
idea 2020.1 + AutoValue 1.7.1
Maven配置
我们需要同时配置auto-value-annotations和auto-value两个dependency
<dependency>
<groupId>com.google.auto.value</groupId>
<artifactId>auto-value-annotations</artifactId>
<version>1.7.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.google.auto.value</groupId>
<artifactId>auto-value</artifactId>
<version>1.7.1</version>
<optional>true</optional>
<scope>provided</scope>
</dependency>
Idea配置
在Build,Execution,Deployment -> Complier -> Annotation Processor中勾选Enable annotation processing
默认的Production source directory和Test sources directory不需要更改

基本用法
如下代码所示,构造Person抽象类,create()静态工厂方法,和抽象的字段方法
@AutoValue
abstract class Person {
abstract String getName();
abstract int getAge();
static Person create(String name, int age) {
return new AutoValue_Person(name, age);
}
}
此时显然还没有AutoValue_Person这个类(固定的前缀写法),因此idea会报错,但是我们暂时不用担心这个问题
编写一个简单的测试类,测试方法如下
@Test
public void test() {
Person person = Person.create("kuluo", 18);
assertEquals("kuluo", person.name());
}
可以先对源代码进行编译而不运行,编译结束后可以看到AutoValue_Person的报错消失
我们可以在默认的target\generated-sources\annotations或target\generated-test-sources\test-annotations文件夹中看到生成的值类
@Generated("com.google.auto.value.processor.AutoValueProcessor")
final class AutoValue_Person extends Person {
private final String name;
private final int age;
AutoValue_Person(String name, int age) {
if (name == null) {
throw new NullPointerException("Null name");
}
this.name = name;
this.age = age;
}
@Override
String getName() {
return name;
}
@Override
int getAge() {
return age;
}
@Override
public String toString() {
return "Person{"
+ "name=" + name + ", "
+ "age=" + age
+ "}";
}
@Override
public boolean equals(Object o) {
if (o == this) {
return true;
}
if (o instanceof Person) {
Person that = (Person) o;
return this.name.equals(that.name()) && this.age == that.age();
}
return false;
}
@Override
public int hashCode() {
int h$ = 1;
h$ *= 1000003;
h$ ^= name.hashCode();
h$ *= 1000003;
h$ ^= age;
return h$;
}
}
注意:
- 值类被声明为final类型,无法再被继承
- 值类不具备setter方法,实例被创建后就无法被更改
- 若在构造实例时允许传入可变类型的值,如List<String>和String[],则需要在Guava中选择对应的不可变类型,并更改create()静态工厂方法
check if the mutable type has a corresponding immutable cousin. For example, the types List<String> and String[] have the immutable counterpart ImmutableList<String> in Guava. If so, use the immutable type for your property, and only accept the mutable type during construction
@AutoValue
abstract class Person {
abstract ImmutableList<String> getName();
abstract int getAge();
static Person create(List<String> name, int age) {
return new AutoValue_Person(ImmutableList.copyOf(name), age);
}
}
- 值类在构建实例时会检查每一个字段是否为null,若某字段为null,则抛出空指针异常
- 若允许某个字段为null,则必须在抽象类create()静态工厂方法的声明中,为该字段和它对应的getter方法同时添加@Nullable注解
if @Nullable is only added to the parameter in create (or similarly the setter method of AutoValue.Builder), but not the corresponding accessor method, it won't have any effect.
@AutoValue
abstract class Person {
@Nullable abstract String getName();
abstract int getAge();
static Person create(@Nullable String name, int age) {
return new AutoValue_Person(name, age);
}
}
下面仅展示发生变化的方法,我们可以看到在equals()方法和hashCode()方法中也自动增加了对null的判断
AutoValue_Person(@Nullable String name, int age) {
this.name = name;
this.age = age;
}
@Nullable
@Override
String getName() {
return name;
}
@Override
public boolean equals(Object o) {
if (o == this) {
return true;
}
if (o instanceof Person) {
Person that = (Person) o;
return (this.name == null ? that.getName() == null : this.name.equals(that.getName()))
&& this.age == that.getAge();
}
return false;
}
@Override
public int hashCode() {
int h$ = 1;
h$ *= 1000003;
h$ ^= (name == null) ? 0 : name.hashCode();
h$ *= 1000003;
h$ ^= age;
return h$;
}
Builder构建者模式用法
涉及抽象静态内部类Builder,并为它添加@AutoValue.Builder注解
@AutoValue
abstract class PersonWithBuilder {
abstract String getName();
abstract int getAge();
static Builder builder() {
return new AutoValue_PersonWithBuilder.Builder();
}
@AutoValue.Builder
abstract static class Builder {
abstract Builder name(String name);
abstract Builder age(int age);
abstract PersonWithBuilder build();
}
}
编写一个简单的测试类,测试代码如下:
@Test
public void testWithBuilder() {
PersonWithBuilder personWithBuilder = PersonWithBuilder
.builder()
.name("kuluo")
.age(18)
.build();
assertEquals("kuluo", personWithBuilder.getName());
assertEquals(18, personWithBuilder.getAge());
}
编译运行后生成AutoValue_PersonWithBuilder类
@Generated("com.google.auto.value.processor.AutoValueProcessor")
final class AutoValue_PersonWithBuilder extends PersonWithBuilder {
private final String name;
private final int age;
private AutoValue_PersonWithBuilder(String name, int age) {
this.name = name;
this.age = age;
}
@Override
String getName() { return name; }
@Override
int getAge() { return age; }
@Override
public String toString() {
return "PersonWithBuilder{"
+ "name=" + name + ", "
+ "age=" + age
+ "}";
}
@Override
public boolean equals(Object o) {
if (o == this) { return true; }
if (o instanceof PersonWithBuilder) {
PersonWithBuilder that = (PersonWithBuilder) o;
return this.name.equals(that.getName()) && this.age == that.getAge();
}
return false;
}
@Override
public int hashCode() {
int h$ = 1;
h$ *= 1000003;
h$ ^= name.hashCode();
h$ *= 1000003;
h$ ^= age;
return h$;
}
static final class Builder extends PersonWithBuilder.Builder {
private String name;
private Integer age;
Builder() {
}
@Override
PersonWithBuilder.Builder name(String name) {
if (name == null) {
throw new NullPointerException("Null name");
}
this.name = name;
return this;
}
@Override
PersonWithBuilder.Builder age(int age) {
this.age = age;
return this;
}
@Override
PersonWithBuilder build() {
String missing = "";
if (this.name == null) {
missing += " name";
}
if (this.age == null) {
missing += " age";
}
if (!missing.isEmpty()) {
throw new IllegalStateException("Missing required properties:" + missing);
}
return new AutoValue_PersonWithBuilder(this.name, this.age);
}
}
}
注意:
- 值类在构建实例时会在build()方法内检查每一个字段是否为null,若某字段为null,则抛出空指针异常
- 若允许某个字段为null,则必须同时在抽象静态内部类的"setter"方法的形参和外侧的"getter"方法同时添加@Nullable注解
@AutoValue
abstract class PersonWithBuilder {
@Nullable abstract String getName();
abstract int getAge();
static Builder builder() {
return new AutoValue_PersonWithBuilder.Builder();
}
@AutoValue.Builder
abstract static class Builder {
abstract Builder name(@Nullable String name);
abstract Builder age(int age);
abstract PersonWithBuilder build();
}
}
我们可以在生成的AutoValue_PersonWithBuilder类中看到已经没有了对name的null判断
@Override
PersonWithBuilder build() {
String missing = "";
if (this.age == null) {
missing += " age";
}
if (!missing.isEmpty()) {
throw new IllegalStateException("Missing required properties:" + missing);
}
return new AutoValue_PersonWithBuilder(this.name, this.age);
}
- 若需要为某字段设置默认值,仅需在builder()方法中调用Builder的"setter"方法
@AutoValue
abstract class PersonWithBuilder {
abstract String getName();
abstract int getAge();
static Builder builder() {
return new AutoValue_PersonWithBuilder.Builder().name("kuluo");
}
@AutoValue.Builder
abstract static class Builder {
abstract Builder name(String name);
abstract Builder age(int age);
abstract PersonWithBuilder build();
}
}
- 使用Builder模式后会屏蔽静态工厂方法,若一定要使用静态工厂方法,则需要在静态工厂方法内调用Builder静态内部类来创建实例,而不是私有的构造方法
AutoValue —— Generated immutable value classes的更多相关文章
- 针对android方法数64k的限制,square做出的努力。精简protobuf
1.早期的Dalvik VM内部使用short类型变量来标识方法的id,dex限制了程序的最大方法数是65535,如果超过最大限制,无法编译,把dex.force.jumbo=true添加到proje ...
- RPC服务框架探索之Thrift
前言架构服务化后,需要实现一套方便调用各服务的框架,现在开源如日中天,优先会寻找开源实现,如果没有合适自家公司业务的,才会考虑从零开发,尤其是一切以KPI为准绳的公司,谁会跟钱过不去?N个月之前,公司 ...
- 【译】Android API 规范
[译]Android API 规范 译者按: 修改R代码遇到Lint tool的报错,搜到了这篇文档,aosp仓库地址:Android API Guidelines. 58e9b5f Project ...
- PHP Excel 下载数据,并分页下载
直接上代码: 调用下载Excel: $total=$duoduo->count(MOD.' as a',$where); $objExcel= SelfExcelObject(); //导出 i ...
- PHP执行文档操作
1.POWINTPOINT系列 之前参与过一个商城的项目,里面有将excel 导出的功能,但是如果要弄成PPT的我们应该怎么办呢?PHP是属于服务器端的 总不能在里面装个Powintpoint吧.于是 ...
- PHP导入导出excel表格图片(转)
写excel的时候,我用过pear的库,也用过pack压包的头,同样那些利用smarty等作的简单替换xml的也用过,csv的就更不用谈了.呵呵.(COM方式不讲了,这种可读的太多了,我也写过利用wp ...
- PHPExcel 大数据的导出
PHPExcel 是一个php语言读取导出数据.导入生成Excel的类库,使用起来非常方便,但有时会遇到以些问题,比如导出的数据超时,内存溢出等. 下面我们来说说这些问题和解决办法. PHPExcel ...
- Beginning Scala study note(6) Scala Collections
Scala's object-oriented collections support mutable and immutable type hierarchies. Also support fun ...
- 黄聪:phpexcel中文教程-设置表格字体颜色背景样式、数据格式、对齐方式、添加图片、批注、文字块、合并拆分单元格、单元格密码保护
首先到phpexcel官网上下载最新的phpexcel类,下周解压缩一个classes文件夹,里面包含了PHPExcel.php和PHPExcel的文件夹,这个类文件和文件夹是我们需要的,把class ...
随机推荐
- Java的诞生历史
Java帝国的诞生 1.1972年C诞生 贴近硬件,运行极快,效率极高 操作系统,编译器,数据库,网络系统等都采用C语言 但是,它的指针和内存管理给程序员安上了"枷锁".它的指针没 ...
- 【CPU】进程管理之五状态模型
本文为第三篇,进程管理之五状态模型,进程在操作系统里边是有多个状态的,本文就是了解进程在操作系统中的多个状态 1.进程的五个状态 创建状态 就绪状态 阻塞状态 执行状态 终止状态 2.进程处于这五种状 ...
- 由浅入深--MyBatis整体架构
学一门技术,做一件事不应该马上就直接进入到细节中,而是应该鸟瞰其全貌,了解它的整体架构和执行流程. MyBatis的整体架构 MyBatis的整体架构分为三层,分别是基础支持层,核心处理层和接口层,各 ...
- /etc/fstab文件的详解
转至:https://blog.csdn.net/youmatterhsp/article/details/83933158 一./etc/fstab文件的作用 磁盘被手动挂载之后都必须把挂载信息写入 ...
- AcWing 288. 休息时间
传送门 思路: 考虑DP,设dp[i][j][1]为牛在前小时休息j个小时且第i个小时休息时,回复的最多体力:dp[i][j][0]为牛在前小时休息j个小时且第i个小时没有休息时,回复的最多体力. 可 ...
- PyTorch深度学习实践——处理多维特征的输入
处理多维特征的输入 课程来源:PyTorch深度学习实践--河北工业大学 <PyTorch深度学习实践>完结合集_哔哩哔哩_bilibili 这一讲介绍输入为多维数据时的分类. 一个数据集 ...
- 01_opencv_python_基本图像处理
1 图像基本操作 1.0.1 环境配置地址: Anaconda:https://www.anaconda.com/download/ Python_whl:https://www.lfd.uc ...
- 关于SQL优化的辟谣
列举一些关于 SQL 语句的谣言,避免一些生瓜蛋子面试的时候被另外一群生瓜蛋子的 SQL 优化宝典给坑了. 以下所有内容都是 SQL Server 中的,其他数据库只能参考和借鉴 一.全表扫描 全表扫 ...
- Java 类方法和类变量
目录 一.类变量 1.如果定义类变量 2.如何访问类变量 3.类变量的使用注意事项和细节 二.类方法 1.类方法的形式 2.类方法的调用 3.类方法经典使用场景 4.类变量和类方法 三.main方法 ...
- 别再问WiFi密码了,HMS Core统一扫码服务让手机一键联网
现代生活离不开网络.在餐厅.商场等公共场所,手机连接WiFi一直是高频使用场景.虽然公共场所的免费WiFi越来越多,但网络连接过程却很麻烦.有的需要打开网页注册或点击广告链接才能上网,还有的要求下载特 ...