/**
* Specifies that <a href="https://github.com/google/auto/tree/master/value">AutoValue</a> should
* generate an implementation class for the annotated abstract class, implementing the standard
* {@link Object} methods like {@link Object#equals equals} to have conventional value semantics. A
* simple example: <pre>
*
* @AutoValue
* abstract class Person {
* static Person create(String name, int id) {
* return new AutoValue_Person(name, id);
* }
*
* abstract String name();
* abstract int id();
* }</pre>
*
* @see <a href="https://github.com/google/auto/tree/master/value">AutoValue User's Guide</a>
*
* @author Éamonn McManus
* @author Kevin Bourrillion
*/
@Retention(RetentionPolicy.CLASS)
@Target(ElementType.TYPE)
public @interface AutoValue {
/**
* Specifies that AutoValue should generate an implementation of the annotated class or interface,
* to serve as a <i>builder</i> for the value-type class it is nested within. As a simple example,
* here is an alternative way to write the {@code Person} class mentioned in the {@link AutoValue}
* example: <pre>
*
* @AutoValue
* abstract class Person {
* static Builder builder() {
* return new AutoValue_Person.Builder();
* }
*
* abstract String name();
* abstract int id();
*
* @AutoValue.Builder
* interface Builder {
* Builder name(String x);
* Builder id(int x);
* Person build();
* }
* }</pre>
*
* @author Éamonn McManus
*/
@Retention(RetentionPolicy.SOURCE)
@Target(ElementType.TYPE)
public @interface Builder {}
/**
* Specifies that AutoValue should copy any annotations from the annotated element to the
* generated class. This annotation supports classes and methods.
*
* <p>The following annotations are excluded:
*
* <ol>
* <li>AutoValue and its nested annotations;
* <li>any annotation appearing in the {@link AutoValue.CopyAnnotations#exclude} field;
* <li>any class annotation which is itself annotated with the
* {@link java.lang.annotation.Inherited} meta-annotation.
* </ol>
*
* <p>When the <i>type</i> of an {@code @AutoValue} property method has annotations, those are
* part of the type, so they are always copied to the implementation of the method.
* {@code @CopyAnnotations} has no effect here. For example, suppose {@code @Confidential} is a
* {@link java.lang.annotation.ElementType#TYPE_USE TYPE_USE} annotation: <pre>
*
* @AutoValue
* abstract class Person {
* static Person create(@Confidential String name, int id) {
* return new AutoValue_Person(name, id);
* }
*
* abstract @Confidential String name();
* abstract int id();
* }</pre>
*
* Then the implementation of the {@code name()} method will also have return type
* {@code @Confidential String}.
*
* @author Carmi Grushko
*/
@Retention(RetentionPolicy.SOURCE)
@Target({ElementType.TYPE, ElementType.METHOD})
public @interface CopyAnnotations {
Class<? extends Annotation>[] exclude() default {};
}
}
가이드라인을 기준으로 클래스 한개를 생성해 볼 것이다.
@AutoValue
public abstract class Product {
public abstract String name();
public abstract BigDecimal price();
@AutoValue.Builder
public abstract static class Builder{
public abstract Builder name(String name);
public abstract Builder price(BigDecimal price);
public abstract Product build();
}
public static Product.Builder builder(){
return new AutoValue_Product.Builder();
}
}
AutoValue 어노테이션을 설정하지 않으면 다음과 같은 오류가 발생한다.
Compiler > Annotation Processors에서 Enable annotation processing을 체크해준다. 그리고 modules의 dependencies에 auto-value 라이브러리가 포함되어있지 않다면 별도로 추가해줘야한다.
모두 추가한 뒤에 project rebuild를 하면 AutoValue_Product가 생성된 것을 볼 수 있다.
다시 코드를 하나하나 집어볼 것이다.
@AutoValue
public abstract class Product {
public abstract String name();
public abstract BigDecimal price();
@AutoValue.Builder
public abstract static class Builder{
public abstract Builder name(String name);
public abstract Builder price(BigDecimal price);
public abstract Product build();
}
public static Product.Builder builder(){
return new AutoValue_Product.Builder();
}
}
@AutoValue 를 사용하는 클래스는 abstract(추상) 클래스이며, 실제로 구현하는 클래스는 builder() 부분의 AutoValue_Product 이다.
안의 클래스 멤버 변수도 abstract 추상함수로 선언되어있는 것을 볼 수 있다. 다음과 같이 선언해주면 AutoValue_Product 클래스에 변수가 생성되는 것을 확인할 수 있다.
import java.math.BigDecimal;
final class AutoValue_Product extends Product {
private final String name;
private final BigDecimal price;
private AutoValue_Product(String name, BigDecimal price) {
this.name = name;
this.price = price;
}
public String name() {
return this.name;
}
public BigDecimal price() {
return this.price;
}
public String toString() {
return "Product{name=" + this.name + ", price=" + this.price + "}";
}
public boolean equals(Object o) {
if (o == this) {
return true;
} else if (!(o instanceof Product)) {
return false;
} else {
Product that = (Product)o;
return this.name.equals(that.name()) && this.price.equals(that.price());
}
}
public int hashCode() {
int h = 1;
int h = h * 1000003;
h ^= this.name.hashCode();
h *= 1000003;
h ^= this.price.hashCode();
return h;
}
static final class Builder extends ch3.dahye.item11.Product.Builder {
private String name;
private BigDecimal price;
Builder() {
}
public ch3.dahye.item11.Product.Builder name(String name) {
if (name == null) {
throw new NullPointerException("Null name");
} else {
this.name = name;
return this;
}
}
public ch3.dahye.item11.Product.Builder price(BigDecimal price) {
if (price == null) {
throw new NullPointerException("Null price");
} else {
this.price = price;
return this;
}
}
public Product build() {
String missing = "";
if (this.name == null) {
missing = missing + " name";
}
if (this.price == null) {
missing = missing + " price";
}
if (!missing.isEmpty()) {
throw new IllegalStateException("Missing required properties:" + missing);
} else {
return new AutoValue_Product(this.name, this.price);
}
}
}
}
public class item11 {
public static void main(String[] args) {
Product product = Product.builder().price(new BigDecimal(990000)).name("autoValue").build();
System.out.println(product); // Product{name=autoValue, price=990000}
}
}
다음과 같이 Product 클래스를 builder로 생성하면, AutoValue_Product 인스턴스로 생성된 것을 볼 수 있다. AutoValue는 불변정보(Immutable)를 전달하는 클래스 인스턴스를 생성할때 사용할 수 있다. AutoValue_Product를 보면 final 클래스이면서 변수도 final 인 것을 볼 수 있다. 그러므로, AutoValue는 멤버 변수 값이 항상 바뀔 수 있는 인스턴스에서는 사용하면 안된다.