Script/Groovy
15. MOP - Compile Time
삽질의 달인
2020. 1. 21. 09:21
앞서 MOP 는 런타임과 컴파일 타임으로 구분되며 컴파일 타임 MOP 는 Groovy 어노테이션인
AST Transform 을 가리킨다고 하였다.
이번 장에서는 AST Transform 을 만드는 것보다
기본적으로 제공되는 여러 어노테이션들을 살펴보도록 하자.
각 어노테이션의 상세 사용법은 아래 페이지를 참조하여 검색하자.
@ToString
기본형으로 @ToString 어노테이션을 사용해도 되는데
이 경우는 아래와 같이 클래스명(입력데이터들) 형식으로 나온다.
import groovy.transform.ToString
@ToString
class Customer {
String first, last
int age
Date since = new Date()
Collection favItems
private answer = 42
}
println new Customer(first:'Tom', last:'Jones', age:21, favItems:['Books', 'Games'])
|
Customer(Tom, Jones, 21, Fri Oct 18 09:03:09 KST 2019, [Books, Games])
|
그래서 속성으로 아래와 같이 컨트롤 할 수 있는데 관련된 속성들이 아주 많다.
기본적으로 아래와 같이 사용할 수 있다는 것만 숙지하고 필요할 때 찾아보도록 하자.
@ToString(includeNames = true, excludes = ["first","last"])
class Customer {
String first, last
int age
Date since = new Date()
Collection favItems
private answer = 42
}
println new Customer(first:'Tom', last:'Jones', age:21, favItems:['Books', 'Games'])
|
Customer(age:21, since:Fri Oct 18 09:06:07 KST 2019, favItems:[Books, Games])
|
실제 자바 클래스가 어떻게 나오는지 확인해 보면
아래와 같이 toString 메서드가 Generate 되어 있는 것을 확인 할 수 있다.
@ToString(
includeNames = true,
excludes = {"first", "last"}
)
public class Customer implements GroovyObject {
private String first;
private String last;
private int age;
private Date since;
private Collection favItems;
private Object answer;
...
@Generated
public String toString() {
CallSite[] var1 = $getCallSiteArray();
Object _result = var1[1].callConstructor(StringBuilder.class);
Object $toStringFirst = Boolean.TRUE;
var1[2].call(_result, "Customer(");
if (DefaultTypeTransformation.booleanUnbox($toStringFirst)) {
Boolean var4 = Boolean.FALSE;
$toStringFirst = var4;
} else {
var1[3].call(_result, ", ");
}
var1[4].call(_result, "age:");
var1[5].call(_result, var1[6].callStatic(InvokerHelper.class, var1[7].callCurrent(this)));
if (DefaultTypeTransformation.booleanUnbox($toStringFirst)) {
Boolean var5 = Boolean.FALSE;
$toStringFirst = var5;
} else {
var1[8].call(_result, ", ");
}
var1[9].call(_result, "since:");
var1[10].call(_result, var1[11].callStatic(InvokerHelper.class, var1[12].callCurrent(this)));
if (DefaultTypeTransformation.booleanUnbox($toStringFirst)) {
Boolean var6 = Boolean.FALSE;
} else {
var1[13].call(_result, ", ");
}
var1[14].call(_result, "favItems:");
var1[15].call(_result, var1[16].callStatic(InvokerHelper.class, var1[17].callCurrent(this)));
var1[18].call(_result, ")");
return (String)ShortTypeHandling.castToString(var1[19].call(_result));
}
...
}
|
Customer(age:21, since:Fri Oct 18 09:06:07 KST 2019, favItems:[Books, Games])
|
@TupleConstructor
앞서 Groovy 의 특징으로 Constructor 를 정의하지 않고 key:value 형식으로 값을 넣으면
기본생성자 + Groovy 의 getProperty 함수가 해당 key 를 찾아 value 를 할당해 준다고 하였다.
이는 편리하지만 기본적으로 멤버명에 해당하는 key 를 넣어주어야 하므로 사용상 불편하다.
그래서 Groovy 에서는 위 어노테이션을 제공하여 이런 불편한 점을 해소해준다.
import groovy.transform.TupleConstructor
@TupleConstructor
class Customer {
String first, last
int age
Date since
Collection favItems
}
def c1 = new Customer(first:'Tom', last:'Jones', age:21, since:new Date(), favItems:['Books', 'Games'])
def c2 = new Customer('Tom', 'Jones', 21, new Date(), ['Books', 'Games'])
def c3 = new Customer('Tom', 'Jones')
|
@TupleConstructor 도 마찬가지로 여러 속성으로 제어 가능하므로 필요시 api 문서를 참조하자.
@Canonical
Canonical 은 @EqualsAndHashCode, @ToString 과 @TupleConstructor 를 조합한 어노테이션이다.
@Singleton
싱글톤 객체를 만들어주는 어노테이션이다.
Generate 된 코드를 보면 SingleTon 에 객체를 생성하고
사용하기 위한 getInstance 메서드와 private 생성자를 볼 수 있다.
@Singleton
class Customer {
static final String first='Daeung'
}
def c1 = Customer.instance
|
public class Customer implements GroovyObject {
private static final String first = "Daeung";
public static final Customer instance;
@Generated
private Customer() {
...
}
@Generated
public static Customer getInstance() {
CallSite[] var0 = $getCallSiteArray();
return instance;
}
...
}
|
@Sortable
해당 어노테이션을 사용하면 Comparable 인터페이스를 상속받아
compareTo 메서드를 자동으로 구현해 주어 객체를 Sort 할 수 있게 해준다.
기본적으로 아래와 같이 사용되는데 따로 어노테이션 프로퍼티를 지정하지 않으면
클래스 프로퍼티 선언 순서 기준으로 sort 메서드를 작동시킨다.
includes 메서드를 사용하면 Sort 기준을 바꾸고 Sort 에 사용할 인자만 지정할 수 있다.
import groovy.transform.Canonical
import groovy.transform.Sortable
@Canonical
@Sortable(includes = ["last","first"])
class Person {
String first
String last
}
def p1 = new Person("Andy","Kim")
def p2 = new Person("Joe", "Lee")
def p3 = new Person("Dan","Vega")
def pList = [p1,p2,p3]
println pList.toSorted()
|
[Person(Andy, Kim), Person(Joe, Lee), Person(Dan, Vega)]
|
@Immutable
클래스 멤버를 초기화 된 이후 상수화 시켜 변경을 막는다.
@TypeChecked
스크립팅 언어의 가장 불편한 점 중 하나는 컴파일 시점에서
해당 코드가 실행이 될 지 여부를 판단하기 힘들다는 점이다.
예를 들어 아래와 같은 상황이 있을 수 있다.
사용자가 멤버에 포함되어 있지 않은 변수를 포함한 함수를 만들어도
getFullName 이 호출되기 전에는 에러가 나지 않는다.
class Person {
String first
String last
String getFullName() {
"$first $Last"
}
}
|
이를 해소하기 위해 @TypeChecked 어노테이션을 사용할 수 있다.
import groovy.transform.TypeChecked
@TypeChecked
class Person {
String first
String last
String getFullName() {
"$first $Last"
}
}
|
Last 가 없다고 에러남
|
@CompileStatic
TypeChecked 와 비슷하지만 TypeChecking 이 필요하지 않은 메소드, 속성 등을 제외시킬수 있다.
import groovy.transform.CompileStatic
import groovy.transform.TypeCheckingMode
@CompileStatic
class Person {
String first
String last
String getFullName() {
"$first $last"
}
@CompileStatic(TypeCheckingMode.SKIP)
def skipMethod(){
"$first $Last"
}
}
|
에러가 나지 않음
|
@Builder
이 어노테이션은 빌더 패턴을 내부적으로 만들어 준다.
Builder 패턴이란 입력할 정보가 많거나 순서가 있을 때 아래와 같이
필요한 순서대로 객체를 만드는 패턴으로 UI 화면을 구성할 때 종종 사용된다.
public class AddStudentMainLayoutFactory {
private class AddStudentMainLayout extends VerticalLayout {
..
public AddStudentMainLayout init() {
..
return this;
}
public AddStudentMainLayout bind() {
..
return this;
}
public Component layout() {
..
return this;
}
}
public Component createComponent() {
return new AddStudentMainLayout().init().bind().layout();
}
}
|
Groovy 에서는 단순히 위 어노테이션을 선언하는 것으로 이와 비슷한 동작을 하게 한다.
import groovy.transform.builder.Builder
@Builder
class Person {
String firstName
String lastName
int age
}
def person = Person
.builder()
.firstName("Robert")
.lastName("Lewandowski")
.age(21)
.build()
assert person.firstName == "Robert"
assert person.lastName == "Lewandowski"
assert person.age == 21
|