업데이트:

5 분 소요

개요

자바를 사용하다보면 수 많은 생성자, getter, setter 등을 만들고 사용하게 된다.
Lombok 라이브러리는 어노테이션으로 이를 간단하게 생성해줄 수 있다.
홈페이지는 https://projectlombok.org/로 접속할 수 있다.

설치

나는 gradle을 사용하고 있기 때문에 이에 맞는 스크립트를 붙여넣어줬다.

lombokpage.png

원하는 언어, IDE에 맞게 설치할 수 있다.

설치는 간단하게 스크립트를 build 설정 파일에 붙여넣으면 된다.

repositories {//build.gradle에 붙여넣어 준다.
	mavenCentral()
}

dependencies {
	compileOnly 'org.projectlombok:lombok:1.18.24'
	annotationProcessor 'org.projectlombok:lombok:1.18.24'
	
	testCompileOnly 'org.projectlombok:lombok:1.18.24'
	testAnnotationProcessor 'org.projectlombok:lombok:1.18.24'
}  

runBy.png

이렇게만 해도 lombok을 사용할 수 있는데, 만약 위 처럼 Build and Using 옵션을 Intellij로 두거나 annotationProcessor 구문이 없다면 아래와 같이 Intellij의 설정에서 따로 Enable Annotation Processing(설정 창에서 검색할 수 있다)를 체크해주고 lombok plugin을 설치해야한다.

enableAnnotationProcessor.png

Lombok

lombok의 어노테이션들이다. 자주 접하고 많이 사용했던 것을 위주로 나열했다.

val

POJO에서는 변수를 선언할 때 명시적으로 타입을 정의해야 하는데, Lombok의 val을 사용함으로써 표현식에서 타입을 추론할 수 있도록 한다.

이 때 필드에는 final 키워드가 자동으로 적용된다.

public class ValExample {
  public String example() {
    val example = new ArrayList<String>();
    example.add("Hello, World!");
    val foo = example.get(0);//값을 get을 통해 얻어낼 수 있다.
    return foo.toLowerCase();
  }
}

var

위의 val과 정확히 같은 기능을 하지만, final 키워드가 적용되지 않는다.

@NonNull

NonNull 어노테이션을 변수와 메서드, 또는 생성자에서 사용함으로써 null-check 구문을 lombok이 대신 만들어준다. 또한 메서드의 가장 윗 단에 이 어노테이션이 존재하면, 추가적인 null-check 구문이 생성되지 않는다.

public static void someMethod(@NonNull String someString){
    System.out.println(someString);
}

null을 감지하면 NullPointerException이 던져진다.

@CleanUp

InputStream 이나 OutputStream을 사용할 때 이들이 사용되고 close 메서드가 존재하지 않고 인자가 존재하지 않는 생성자가 존재한다면, Cleanup 안에 메서드 이름을 특정해 설정해 줄 수 있다.

@CleanUp("someMethod")

@Getter/Setter

별 다른 코드 없이 Getter와 Setter를 생성해준다.
AccessLevel을 통해 접근제한자를 설정해줄 수 있다.

@Getter
@Setter(AccessLevel.PRIVATE)
public class SomeClass{
    private int someInt = 5;
    private String someString = "Hello";
}
public class Main{
    public static void main(String[] args){
        SomeClass s = new SomeClass();
        System.out.println(s.getSomeInt());
    }
}  
====================================================================
5

@ToString

toString() 메서드의 구현을 lombok이 대신해서 만들어준다.
노출되기를 원치 않는 필드가 있다면 exclude 옵션을 사용해주면 된다. 또한 callSuper 옵션을 true로 설정해주면, 상위클래스의 ToString도 호출한다.

@ToString
public class Creature {
    public String species;
}

@ToString(callSuper = true)
public class Person extends Creature{
    private String someName = "kim";
    private int age = 15;
    private int height = 160;
    @ToString.Exclude private int weight = 48;

    public Person() {
        super.species = "Human";
    }
}

public class Main {
    public static void main(String[] args) {
        Person person = new Person();
        System.out.println(person.toString());
    }
}

======================================================================

Person(super=Creature(species=Human), someName=kim, age=15, height=160)

@EqualsAndHashCode

hashCode와 equals의 구현을 lombok이 대신 생성해준다.

@EqualsAndHashCode(exclude = "weight")
@Getter
public class Kim extends Creature{
    private int height = 160;
    @Setter
    private int weight = 49;

    public Kim() {
        super.species = "Human";
    }
}

public class Main {
    public static void main(String[] args) {
        Kim kim = new Kim();
        kim.setWeight(700);
        Kim kim2 = new Kim();
        kim2.setWeight(500);

        System.out.println("kim = " + kim.hashCode());
        System.out.println("lee = " + kim2.hashCode());
        System.out.println(kim.equals(kim2));
    }
}
========================================================
kim = 219
lee = 219
true

@NoArgsConstructor\@RequiredArgsConstructor\@AllArgsConstructor

이 어노테이션들은 생성자를 구현한다.

각각 NoArgsConstructor는 인자가 존재하지 않는 생성자, RequiredArgsCunstructor는 final, @NonNull이 정의된 필드만을 인자로 삼는 생성자, AllArgsConstructor는 모든 필드를 인자로 삼는 생성자를 생성한다.

Getter와 Setter 어노테이션과 같이 access 옵션에 AccessLevel.PROTECTED 처럼 value 값을 주면 접근지정자를 설정할 수 있다.

또한 정적 팩토리 메서드의 이름을 입력받는 staticName 옵션을 줄 수 있다.
생성자가 private일 때 이 옵션을 주어 private 생성자를 감싸고있는 정적 팩토리 메서드를 생성할 수 있다.

@ToString
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class SomeClass{
    public int someInt; 
}
@ToString
@RequiredArgsConstructor(staticName = "of")
public class SomeClass2{
    private final String someString;
    private final int someInt;
}
@ToString
@AllArgsConstructor
public class someClass3{
    public int someInt;
}

public class Main{
    public static void main(String[] args){
        SomeClass s1 = new SomeClass();
        SomeClass2 s2 = new SomeClass2.of("Hello",3);
        SomeClass3 s3 = new SomeClass3(5);

    }
}

@AllArgsConstructor@RequiredArgsConstructor는 사용할 때 주의해야 한다.
이 둘은 클래스 내에서 정의된 필드의 순서대로 생성자를 생성하는데, 나중에 이 순서를 바꿔버리면 치명적인 버그가 발생하고 Lombok을 사용했기 때문에 IDE가 제공하는 리팩토링은 작동하지 않아 버그를 찾기도 쉽지않기 때문이다.

@AllArgsConstructor
public class Person{  //name과 age 순으로 인자를 받는 생성자 생성. 순서가 바뀌면..
    public String name;
    public int age;
}

@Data

@ToString, @EqualsAndHashCode, @Getter, @Setter, @RequiredArgsConstructor과 같은 어노테이션들이 묶여져있는 어노테이션이다.
@Getter의 경우 모든 필드에 적용되며 @Setter의 경우 final이 지정되지 않은 필드에 한해 적용된다.

다만, @RequiredArgsConstructor@EqualsAndHashCode가 함께 포함 되어있기 때문에 모두 사용할 것이 아니면 따로 아래와 같이 사용하는게 좋다고 한다.

@Getter
@Setter
@ToString
public class SomeClass{
    ...
}

@Value

@Data에 불변성이 추가된 어노테이션이다.
모든 필드들이 privatefinal로 만들어지며(클래스 자기자신 또한 private으로 선언된다.) Setter()가 생성되지 않는다.
하지만 @Data처럼 @toString, @EqualsAndHashCode가 적용된다.
즉, @Value는 아래와 같다.

@ToString
@EqualsAndHashCode  
@AllArgsConstructor
@FieldDefaults(makeFinal = true, level = AccessLevel.PRVATE)
@Getter
public class SomeClass{
    ...
}

이 때 AccessLevel값을 바꾸어주거나, @NonFinal 또는 @PackagePrivate 을 사용해 final이나 private을 초기값으로 정하는 행위를 오버라이드할 수 있다고 한다.

@Data와 비슷하기 때문에 역시 같은 주의점이 있고, @Value에서는 @AllArgsConstructor를 포함하기 때문에 필드마다 따로 private final을 설정하고 @Getter@Tostring만 사용하는게 낫다고한다.

@Builder

@Builder 어노테이션을 적용시키는 것 만으로 빌더 패턴을 적용시켜준다.


public class Person{
    private String name;
    private int age;

    @Builder
    public Person(String name, int age){
        this.name = name;
        this.age = age;
    }
}

public class Main{
    public static void main(String[] args){
        Person somePerson = Person.builder().
        .name("Kim")
        .age(16)
        .build();
    }
}  

@Builder@AllArgsConstructor를 가지고 있기 때문에 해당 클래스의 다른 메소드에서 자동 생성된 생성자를 사용할 때 문제가 발생할 소지가 있다고 한다.
따라서 클래스 자체에 @Builder를 적용시키기 보다는 생성자 또는 static 객체 생성 메소드에 적용시키는 것이 좋다 고 한다.

@Synchronized

메소드에 사용되는 어노테이션으로 기존에 제공되는 synchronized 보다 세밀한 설정을 해줄 수 있다. synchronized 키워드는 static과 instance 단위로 락을 걸지만 어노테이션 @Synchronized는 변수로 입력 받는 Object 단위로 락을 걸게된다.

이 때 입력받는 변수가 없으면 @Synchronized가 적용된 메소드 단위로 락을 건다.

@With

불변 속성에 setter를 사용하기 위한 좋은 대안은 해당 필드에 새로운 값을 부여하며 객체의 복제를 만드는 것인데, 이 복제품을 만드는 메서드가 @With이 생성하는 것이다.
lombok document에 작성되어있는 아래 예제를 통해 vanilla java와 비교하면 이해가 쉽다.
Lombok의 경우

public class WithExample {
  @With(AccessLevel.PROTECTED) @NonNull private final String name;
  @With private final int age;
  
  public WithExample(@NonNull String name, int age) {
    this.name = name;
    this.age = age;
  }
}

Vanilla Java의 경우

public class WithExample {
  private @NonNull final String name;
  private final int age;

  public WithExample(String name, int age) {
    if (name == null) throw new NullPointerException();
    this.name = name;
    this.age = age;
  }

  protected WithExample withName(@NonNull String name) {
    if (name == null) throw new java.lang.NullPointerException("name");
    return this.name == name ? this : new WithExample(name, age);
  }

  public WithExample withAge(int age) {
    return this.age == age ? this : new WithExample(name, age);
  }
}

Lombok의 경우 @With을 사용해 private final이 지정된 필드를 protected 접근지정자가 사용된 메서드를 통해 바꿀 수 있는데 반해 vanilla Java의 경우 이를 생성해주는 메서드를 직접 생성해주고 있다.
@With을 사용하면 불변 필드에 대한 값 설정을 간단히 해줄 수 있다.

@Log

이 어노테이션을 적용함으로써 static final log 필드를 생성할 수 있다. 이렇게 만들어진 log는 직접 지정한 로깅 프레임워크를 명시해 초기화될 수 있다.

//@Log
@Log4j
//@Slf4j
// 종류는 Lombok Docs에서 확인할 수 있고, 원하는 프레임워크를 골라쓰면 된다.
public class SomeClass{
    ...
}

참고문서

Lombok
https://projectlombok.org/features/all

Blog
https://partnerjun.tistory.com/53

https://dingue.tistory.com/14

https://kwonnam.pe.kr/wiki/java/lombok/pitfall

태그:

카테고리:

업데이트:

댓글남기기