什么是AutoValue?
AutoValue是一个可以自动为值类(value type)生成诸如equals,hashCode,toString等模板方法的工具。这样就使得程序更加短小,简洁,以及更少的bug。
为什么要使用AutoValue
什么是值类(value type)
这里指的是具有值语义的类型,只要两个实例具有相同的字段值,他们就可以互换。比如DateTime,Money,Uri... 这必须实现equals方法和hashCode方法,通常还会实现toString方法。
看一下实现这样一个值类的基本框架
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
+ "}";
}
}
简单的一个类,添加了equals,hashCode,toString方法之后一下子暴增了,并且添加一个字段之后,你不得不修改这几个非常公式化的方法。最坏的情况,添加类似一个值类,你又得重复这些烦人的工作。
为什么会出现这么多的代码?
1,固有的字段构造函数和访问函数
2,hashCode方法的主要作用是为了配合基于散列的集合一起正常运行,这样的散列集合包括HashSet、HashMap以及HashTable。当向散列集合中插入对象时,先调用这个对象的hashCode方法,得到对应的hashcode值,实际上在HashMap的具体实现中会用一个table保存已经存进去的对象的hashcode值,如果table中没有该hashcode值,它就可以直接存进去,不用再进行任何比较了;如果存在该hashcode值, 就调用它的equals方法与新元素进行比较,相同的话就不存了,不相同就散列其它的地址,所以这里存在一个冲突解决的问题,这样一来实际调用equals方法的次数就大大降低了。
看一个例子
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 hashMap = new HashMap();
hashMap.put(p, 1);
System.out.println(hashMap.get(new People("Jack", 12)));
你觉得会输出1吗?答案是输出null,因为People类没有覆写hashCode方法,导致new 的People与hashMap中的people的hashCode值不一样。所以正确的做法是覆写hashCode方法。
3,equals方法比较两个对象是否相同。
4,toString可以使日志,调式更方便。
怎样使用AutoValue
编写一个类
1,这个类是抽象的
2,抽象的字段访问函数,没有字段。
3,提供一个静态的创建函数返回该类对象
4,类上标记@AutoValue注解
@AutoValue
public abstract class Foo {//1,这个类是抽象的
//3,提供一个静态的创建函数返回该类对象
public static Foo create(String text, int number) {
return new AutoValue_Foo(text, number);//autovalue生成的类名为AutoValue_Foo
}
/** Documentation here. */
public abstract String text(); //2,抽象的字段访问函数,没有字段。
/** 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模式,那么也可以
编写一个类
1,这个类是抽象的
2,抽象的字段访问函数,没有字段。
3,提供一个静态抽象内嵌类Builder,打上@ @AutoValue.Builder注解
4,静态抽象内嵌类Builder提供抽象字段设置方法
5,静态抽象内嵌类Builder提供抽象build方法,返回值是外部类
6,提供一个静态的builder方法返回内嵌类Builder
7,类上标记@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);
}
}
}