아래 글은 Lombok 1.16.16 기반에서 작성되었다.
Lombok에서 @Builder를 사용하다 보니 상속 구조에서 불필요한 코드를 작성해야 하는 경우가 있었다.
예를 들어 아래와 같이 두 모델 객체가 있다고 하자.
@Data
@AllArgsConstructor
public class User {
protected String id;
protected String name;
}
|
@Data
public class AdminUser extends User {
private String roleId;
private String roleName;
@Builder
public AdminUser(String id, String name, String roleId, String roleName) {
super(id, name);
this.roleId = roleId;
this.roleName = roleName;
}
}
|
@Builder를 사용하면서 상속 구조를 가지려면 AdminUser와 같이 생성자를 생성해 주어야 한다.
public class AdminUserTest {
@Test public void 어드민_사용자_생성() {
AdminUser adminUser = AdminUser.builder()
.id("id")
.name("name")
.roleId("roleId")
.roleName("roleName")
.build();
System.out.println(adminUser);
System.out.println(adminUser.getId());
}
}
|
실행 결과를 보면 AdminUser에 id, name 속성이 노출되지 않는다. 이는 Lombok이 아래와 같이 toString()을 구현해 두어서 그런 것이다. 실제로 id, name에 접근이 가능하다.
public String toString() {
return "AdminUser(roleId=" + this.getRoleId() + ", roleName=" + this.getRoleName() + ")";
}
|
위 방법 외에도 User(부모 클래스)에 기본 생성자를 만들어 두고 AdminUser(자식 클래스)에서 가져다 사용하는 방법도 있으나, 어떤 방법이든 생성자를 별도로 만들어 줘야 하는 불편함이 있다.
상속이 아닌 합성(Composite)으로 위와 같은 기능을 구현할 수 있다. (상속과 합성의 차이는 별도로 찾아보자.)
합성을 이용하면 별도의 생성자를 만들지 않아도 @Builder를 사용할 수 있다.
다만 부모 클래스의 빌더에서 지원하는 함수는 함께 사용할 수 없다. 위 예제처럼 id(“id”) 나 name(“name”)은 적용할 수 없다는 것이다.
예제를 살펴보자.
@Data
@Builder
public class User {
protected String id;
protected String name;
}
|
@Data
@Builder
public class AdminUser {
@Delegate
private User user;
private String roleId;
private String roleName;
}
|
public class AdminUserTest {
@Test
public void 어드민_사용자_생성() {
AdminUser adminUser = AdminUser.builder()
.id("id")
.name("name")
.roleId("roleId")
.roleName("roleName")
.build();
System.out.println(adminUser);
}
}
|
public class AdminUserTest {
@Test
public void 어드민_사용자_생성() {
AdminUser adminUser = AdminUser.builder()
.user(User.builder().id("id").name("name").build())
.roleId("roleId")
.roleName("roleName")
.build();
System.out.println(adminUser);
System.out.println(adminUser.getId());
}
}
|
붉은 부분의 코드는 컴파일 오류가 발생한다. @Delegate를 사용한 경우 .user()로 별도의 User 객체를 만들어 주어야 한다.
속성 접근 같은 getId(), getName() 모두 자식 클래스에서 사용할 수 있다.
@Delegate에는 exclude 옵션이 있는데, 이는 모든 method를 위임하지 않고 특정 메서드는 제외하고 AdminUser에서 구현하여 사용할 수도 있다.
예제는 lombok @Delegate 공식 문서를 참고하자.
결론
상속이든 합성이든 장단점이 있는 것 같다.
생성자를 별도로 만들어 주던지 아니면 Builder시 객체를 생성해서 넣어주던지 둘 중에 어느 방법이 나을지는 개인의 판단에 달린 것 같다. 개인마다 다르겠지만 요즘은 상속보다는 합성을 더 많이 사용하는 경향이 있는 것 같은 느낌이다.(근거는 없다… 개인적인 의견)
P.S 나중에라도 상속 또는 합성에서 별도의 작업을 하지 않고 적용하는 방법을 찾아봐야겠다.
참고
- http://stackoverflow.com/questions/32989562/how-to-build-an-object-when-we-have-inheritance-relationship-using-lombok-builde
- https://reinhard.codes/2015/09/16/lomboks-builder-annotation-and-inheritance/
- https://projectlombok.org/features/Delegate.html
'Programing > Java' 카테고리의 다른 글
Jackson을 이용한 org.joda.time.DateTime 파싱하기 (0) | 2017.07.27 |
---|---|
Jackson을 이용하여 JSON -> POJO 변환시 POJO에 존재하지 않는 property가 있는 경우 (0) | 2017.07.21 |
Lombok @Builder 사용시 기본값 지정하기 (0) | 2017.04.12 |
HashMap, Hashtable, ConcurrentHashMap 동기화 처리 방식 (0) | 2016.12.20 |
HashMap, TreeMap 그리고 LinkedHashMap의 차이 (1) | 2016.12.19 |