[JAVA] Lombok 정리
업데이트:
개요
자바를 사용하다보면 수 많은 생성자, getter, setter 등을 만들고 사용하게 된다.
Lombok 라이브러리는 어노테이션으로 이를 간단하게 생성해줄 수 있다.
홈페이지는 https://projectlombok.org/로 접속할 수 있다.
설치
나는 gradle을 사용하고 있기 때문에 이에 맞는 스크립트를 붙여넣어줬다.
원하는 언어, 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'
}
이렇게만 해도 lombok을 사용할 수 있는데, 만약 위 처럼 Build and Using 옵션을 Intellij로 두거나 annotationProcessor 구문이 없다면 아래와 같이 Intellij의 설정에서 따로 Enable Annotation Processing(설정 창에서 검색할 수 있다)를 체크해주고 lombok plugin을 설치해야한다.
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
에 불변성이 추가된 어노테이션이다.
모든 필드들이 private
와 final
로 만들어지며(클래스 자기자신 또한 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
댓글남기기