使用 Google AutoValue 自动生成代码
原文
http://tedyin.me/2016/04/11/auto-value/
主题 谷歌
在 Java
中通常定义一个模型类时,需要定义一堆不同类型的成员变量,而且为了满足面向对象的基本特征,又要定义一堆相应的 Getter
和 Setter
等方法,这个过程是非常枯燥的要写一堆样板代码。虽然现在 IDE 可以很好的帮我们生成这些样板代码,但是如果看到一个模型类里面有这么一堆的方法,总觉是会觉得不够清晰。
我只想知道这个模型有哪些属性,以及特殊的方法,并不想知道大家都有的东西,你却非要让我看。。。
好了,现在有办法可以解决这个问题了,使用 Google 开源的 AutoValue 就可以解决上述烦恼,而且最近 AutoValue
项目支持了大家期待已久的 Extension API
,使得 AutoValue
更加灵活,至于这个 Extension API
是干啥的后面会讲到,现在暂时不用去关心他。
普通的 Java 模型
我们这里举个故事(Story)的例子,一个故事模型拥有一个 id
,以及一个 title
。下面我们来看下用 Java
代码来表示该模型的写法。
不太严谨的写法
我们先来看下一般情况下我们是怎么定义这个 POJO 的业务模型的。
public class Story{ public int id; public String title;}
上面的写法非常简单,不过有些情况下可能会有问题,比如对象比较时,而且也不符合封装的要求,不过一般情况下业务逻辑简单,还处在快速迭代的时候这样写也没什么问题,而且重点是他 只有4行代码!
比较规范的写法
下面看下正常情况下的写法:
public class Story{ private int id; private String title; public Story(int id, String title){ this.id = id; this.title = title; } public int id(){ return this.id; } public int title(){ return this.title; }}
这样写符合了面向对象的基本特征 封装
的要求,但是如果这个 Story
是在列表或者集合中有用到比较的时候,这么写是有问题的,应该继续重载 hashCode()
和equals()
方法,如果有特殊格式的输出,还得重载 toString()
方法。
把这些东西都补全的写法如下:
public class Story{ private int id; private String title; public Story(int id, String title){ this.id = id; this.title = title; } public int id(){ return this.id; } public int title(){ return this.title; } @Override public String toString(){ return id + title; } @Override public int hashCode() { int hash = 7; hash = 31 * hash + this.id; hash = 31 * hash + (null == title ? 0 : title.hashCode()); return hash; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || !(o instanceof Stroy)) return false; Story s = (Story) o; if (s.id != s.id) return false; return s.title.equals(s.title); }}
上面的版本是一个比较完整的模型写法,相比起最初的版本,代码多了不少从原来的 4
行变成了现在的 39
行!
OMG!现在的版本还没有实现 Parcelable
接口,如果再在他的基础上实现Parcelable
接口,那代码又得增加十几行,想想都心累啊。
使用AutoValue拯救你的代码
我本想要一个只有几行的模型类,但是出于各方面原因,我得到了一个几十行代码的模型类,为了解决这个问题伟大的 Google
开发了 AutoValue
这个库来自动生成这些样板代码,解放我们的双手,让我们把更多的精力放在更重要的事情上。
使用AutoValue
使用方法很简单,只需要在你的项目中引入 AutoValue
的插件即可
<pre class="prettyprint undefined" name="code" style="box-sizing: border-box; outline: 0px; margin: 0px 0px 1.5em; padding: 0.3em; font-weight: normal; position: relative; overflow: auto; border-top: none; border-right: none; border-bottom: 1px solid rgb(245, 246, 247); border-left: none; border-image: initial; white-space: pre-wrap; overflow-wrap: break-word; font-family: Monaco, Menlo, Consolas, "Courier New", monospace; font-size: 12px; line-height: 1.5em; color: rgb(0, 0, 0); background-color: rgb(246, 246, 246); z-index: 9; word-break: break-all;">dependencies {
apt 'com.google.auto.value:auto-value:1.2'
}
</pre>
然后在代码中使用 @AutoValue
注解来指定哪个类需要生成类似于上面的样板代码。下面我们使用 AutoValue
来重新实现一下 Story
模型。
@AutoValuepublic abstract class Story{ public abstract int id(); public abstract String title();}
使用 @AutoValue
注解后, AutoValue
会生成一个 AutoValue_你的类名
为名称的类,这个类是 包级私有
的,他里面有私有的成员变量,对应的构造函数,以及重写的 hashCode()
、 equals()
和 toString()
方法,而且这些方法都是被测试过的确保无误的,你可以放心的使用。由于这个生成的子类是 包级私有
的,所以这里在给 Story
提供构造方法的时候需要提供一个静态的构造方法,代码如下:
@AutoValuepublic abstract class Story{ public abstract int id(); public abstract String title(); public static Story create(int id, String title){ new AutoValue_Story(id,title); }}
好了,使用AutoValue后,这短短几行代码就完成了上面几十行代码干的事,而且这样生成出来的代码都是被测试过准确无误的,这也避免了一些因为手误或者逻辑错误导致的BUG的产生,是不是很爽?必须很爽啊!
But… 我们要更爽一点!
AutoValue Extension API
大家注意到没有上面使用 AutoValue
实现的 Story
模型并没有实现Parcelable
接口,那如果要实现这个接口是不是又得一堆代码呢?当然不会,因为我们有 Extension API
,因为有了他我们可以使用基于他实现出来的 AutoValue: Parcel Extension 来实现 Parcelable
接口。
使用 Parcel Extension 实现 Parcelable 接口
首先我们需要在项目中集成 AutoValue Parcel Extension
<pre class="prettyprint undefined" name="code" style="box-sizing: border-box; outline: 0px; margin: 0px 0px 1.5em; padding: 0.3em; font-weight: normal; position: relative; overflow: auto; border-top: none; border-right: none; border-bottom: 1px solid rgb(245, 246, 247); border-left: none; border-image: initial; white-space: pre-wrap; overflow-wrap: break-word; font-family: Monaco, Menlo, Consolas, "Courier New", monospace; font-size: 12px; line-height: 1.5em; color: rgb(0, 0, 0); background-color: rgb(246, 246, 246); z-index: 9; word-break: break-all;">dependencies {
provided 'com.google.auto.value:auto-value:1.2'
apt 'com.google.auto.value:auto-value:1.2'
apt 'com.ryanharter.auto.value:auto-value-parcel:0.2.1'
}
</pre>
然后再在代码中加入 implements Parcelable
即可
@AutoValuepublic abstract class Story implements Parcelable{ public abstract int id(); public abstract String title(); public static Story create(int id, String title){ new AutoValue_Story(id,title); }}
是不是已经体会到了 AutoValue Extension
的厉害了?反正我是觉得碉堡了!
有了这个扩展功能,能干的事情就多了,常规的像对象序列化成 JSON 字符串,将 JSON 字符串解析成对象等这些事情就不用自己再去动手去写了,直接用 AutoValue
生成就可以了,再也不用去写那些 toJson()
和 fromJson
之类的方法了,真是爽歪歪啊!
</article>