什么是AutoValue?
AutoValue是一個(gè)可以自動(dòng)為值類(value type)生成諸如equals,hashCode,toString等模板方法的工具。這樣就使得程序更加短小,簡潔,以及更少的bug。
為什么要使用AutoValue
什么是值類(value type)
這里指的是具有值語義的類型,只要兩個(gè)實(shí)例具有相同的字段值,他們就可以互換。比如DateTime,Money,Uri... 這必須實(shí)現(xiàn)equals方法和hashCode方法,通常還會(huì)實(shí)現(xiàn)toString方法。
看一下實(shí)現(xiàn)這樣一個(gè)值類的基本框架
public final class Foo {
private final String text;
private final int number;
public Foo(String text, int number) {
this.text = text;
this.number = number;
}
public String text() {
return text;
}
public int number() {
return number;
}
@Override
public boolean equals(@Nullable Object o) {
if (o instanceof Foo) {
Foo that = (Foo) o;
return text.equals(that.text)
&& number == that.number;
}
return false;
}
@Override
public int hashCode() {
int h$ = 1;
h$ *= 1000003;
h$ ^= text.hashCode();
h$ *= 1000003;
h$ ^= number;
return h$;
}
@Override
public String toString() {
return "Foo{"
+ "text=" + text + ", "
+ "number=" + number
+ "}";
}
}
簡單的一個(gè)類,添加了equals,hashCode,toString方法之后一下子暴增了,并且添加一個(gè)字段之后,你不得不修改這幾個(gè)非常公式化的方法。最壞的情況,添加類似一個(gè)值類,你又得重復(fù)這些煩人的工作。
為什么會(huì)出現(xiàn)這么多的代碼?
1,固有的字段構(gòu)造函數(shù)和訪問函數(shù)
2,hashCode方法的主要作用是為了配合基于散列的集合一起正常運(yùn)行,這樣的散列集合包括HashSet、HashMap以及HashTable。當(dāng)向散列集合中插入對象時(shí),先調(diào)用這個(gè)對象的hashCode方法,得到對應(yīng)的hashcode值,實(shí)際上在HashMap的具體實(shí)現(xiàn)中會(huì)用一個(gè)table保存已經(jīng)存進(jìn)去的對象的hashcode值,如果table中沒有該hashcode值,它就可以直接存進(jìn)去,不用再進(jìn)行任何比較了;如果存在該hashcode值, 就調(diào)用它的equals方法與新元素進(jìn)行比較,相同的話就不存了,不相同就散列其它的地址,所以這里存在一個(gè)沖突解決的問題,這樣一來實(shí)際調(diào)用equals方法的次數(shù)就大大降低了。
看一個(gè)例子
class People{
private String name;
private int age;
public People(String name,int age) {
this.name = name;
this.age = age;
}
public void setAge(int age){
this.age = age;
}
@Override
public boolean equals(Object obj) {
// TODO Auto-generated method stub
return this.name.equals(((People)obj).name) && this.age== ((People)obj).age;
}
}
People p = new People("Jack", 12);
System.out.println(p.hashCode());
HashMap<People, Integer> hashMap = new HashMap<People, Integer>();
hashMap.put(p, 1);
System.out.println(hashMap.get(new People("Jack", 12)));
你覺得會(huì)輸出1嗎?答案是輸出null,因?yàn)镻eople類沒有覆寫hashCode方法,導(dǎo)致new 的People與hashMap中的people的hashCode值不一樣。所以正確的做法是覆寫hashCode方法。
3,equals方法比較兩個(gè)對象是否相同。
4,toString可以使日志,調(diào)式更方便。
怎樣使用AutoValue
編寫一個(gè)類
1,這個(gè)類是抽象的
2,抽象的字段訪問函數(shù),沒有字段。
3,提供一個(gè)靜態(tài)的創(chuàng)建函數(shù)返回該類對象
4,類上標(biāo)記@AutoValue注解
@AutoValue
public abstract class Foo {//1,這個(gè)類是抽象的
//3,提供一個(gè)靜態(tài)的創(chuàng)建函數(shù)返回該類對象
public static Foo create(String text, int number) {
return new AutoValue_Foo(text, number);//autovalue生成的類名為AutoValue_Foo
}
/** Documentation here. */
public abstract String text(); //2,抽象的字段訪問函數(shù),沒有字段。
/** Documentation here. */
public abstract int number();
}
AutoValue替我們生成的類
final class AutoValue_Foo extends Foo { //注意:該類不是public
private final String text;
private final int number;
AutoValue_Foo(String text, int number) {
if (text == null) {
throw new NullPointerException("text");
}
this.text = text;
this.number = number;
}
@Override public String text() {
return text;
}
@Override public int number() {
return number;
}
@Override
public boolean equals(@Nullable Object o) {
if (o instanceof Foo) {
Foo that = (Foo) o;
return text.equals(that.text)
&& number == that.number;
}
return false;
}
@Override
public int hashCode() {
int h$ = 1;
h$ *= 1000003;
h$ ^= text.hashCode();
h$ *= 1000003;
h$ ^= number;
return h$;
}
@Override
public String toString() {
return "Foo{"
+ "text=" + text + ", "
+ "number=" + number
+ "}";
}
}
如果你想使用Builder模式,那么也可以
編寫一個(gè)類
1,這個(gè)類是抽象的
2,抽象的字段訪問函數(shù),沒有字段。
3,提供一個(gè)靜態(tài)抽象內(nèi)嵌類Builder,打上@ @AutoValue.Builder注解
4,靜態(tài)抽象內(nèi)嵌類Builder提供抽象字段設(shè)置方法
5,靜態(tài)抽象內(nèi)嵌類Builder提供抽象build方法,返回值是外部類
6,提供一個(gè)靜態(tài)的builder方法返回內(nèi)嵌類Builder
7,類上標(biāo)記@AutoValue注解
@AutoValue
public abstract class Foo {
abstract String text();
abstract int number();
public static Builder builder(){
return new AutoValue_Foo.Builder();
}
@AutoValue.Builder
abstract static class Builder{
abstract Builder setText(String text);
abstract Builder setNumber(int number);
abstract Foo build();
}
}
AutoValue替我們生成的類
@Generated("com.google.auto.value.processor.AutoValueProcessor")
final class AutoValue_Foo extends Foo {
private final String text;
private final int number;
private AutoValue_Foo(
String text,
int number) {
this.text = text;
this.number = number;
}
@Override
String text() {
return text;
}
@Override
int number() {
return number;
}
@Override
public String toString() {
return "Foo{"
+ "text=" + text + ", "
+ "number=" + number
+ "}";
}
@Override
public boolean equals(Object o) {
if (o == this) {
return true;
}
if (o instanceof Foo) {
Foo that = (Foo) o;
return (this.text.equals(that.text()))
&& (this.number == that.number());
}
return false;
}
@Override
public int hashCode() {
int h$ = 1;
h$ *= 1000003;
h$ ^= text.hashCode();
h$ *= 1000003;
h$ ^= number;
return h$;
}
static final class Builder extends Foo.Builder {
private String text;
private Integer number;
Builder() {
}
@Override
Foo.Builder setText(String text) {
if (text == null) {
throw new NullPointerException("Null text");
}
this.text = text;
return this;
}
@Override
Foo.Builder setNumber(int number) {
this.number = number;
return this;
}
@Override
Foo build() {
String missing = "";
if (this.text == null) {
missing += " text";
}
if (this.number == null) {
missing += " number";
}
if (!missing.isEmpty()) {
throw new IllegalStateException("Missing required properties:" + missing);
}
return new AutoValue_Foo(
this.text,
this.number);
}
}
}