Lombok Annotation

@Getter / @Setter

Annotation Getter Setter 함수를 생성해준다. 그리고  AccessLevel(PUBLIC, PROTECTED, PACKAGE, PRIVATE) 지정을 통해서 접근 레벨도 제한할 있으며, 특정 필드는 메서드 생성이 필요하지 않을 경우 AccessLevel.NONE 설정을 통해 자동 메소드 생성을 막을 있다.

 

아래 예제 소스를 통해 어떻게 활용하는지 알아보자.

Lombok Code

 

1

2

@Getter @Setter private boolean employed = true;

@Setter(AccessLevel.PROTECTED) private String name;

cs

 

Java Code

 

1

2

3

4

5

6

7

8

9

10

11

12

13

14

private boolean employed = true;

private String name;

 

public boolean isEmployed() {

    return employed;

}

 

public void setEmployed(final boolean employed) {

    this.employed = employed;

}

 

protected void setName(final String name) {

    this.name = name;

}

Colored by Color Scripter

cs

 

 

@NonNull

@NonNull Class위에서는 사용이 불가능하며, Class 내에서 필요한 멤버에 직접 사용해야 한다. @NonNull 사용하면 @Setter 생성되는 setter 메서드에 전달되는 parameter @NonNull 붙게 된다. 하지만 해당 setter 메서드에 null 넘기도록 코드를 작성 수도 있다. , 컴파일 시점에서 적용되지 않고 runtime시에 Null Check 하여 NullPointerException 발생하도록 하는 것이다. , setXXX null을 넘겨줄 경우 if (xxx==null) 체크 코드가 들어가서 throw new NullPointerException("XXX")을 던져준다. 멤버 변수에 붙여줄 경우에는 그 필드를 꼭 받도록 생성자가 수정된다.

 

Lombok Code

1

2

@Getter @Setter @NonNull

private List<Person> members;

cs

 

Java Code

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

@NonNull

private List<Person> members;

 

public Family(@NonNull final List<Person> members) {

    if (members == nullthrow new java.lang.NullPointerException("members");

    this.members = members;

}

    

@NonNull

public List<Person> getMembers() {

    return members;

}

 

public void setMembers(@NonNull final List<Person> members) {

    if (members == nullthrow new java.lang.NullPointerException("members");

    this.members = members;

}

Colored by Color Scripter

cs

 

 

@ToString

@ToStringClass에 있는 필드들을 검사해서 문자열로 변환해주는 toString() 메소드를 생성한다. 상호 참조하는 객체의 경우에는 toString() 호출시 Stack Overflow 가 발생할 수 있다. 한쪽 객체에서 다른 쪽 객체에 대해 @ToString(excluded={“propertyName”}) 형태로 제외토록 만들어야 한다.

@EqualsAndHashCode(of = {})로 꼭 필요한 필드만 비교하도록 처리한다.

 

 

Lombok Code

1

2

3

4

5

6

@ToString(callSuper=true,exclude="someExcludedField")

public class Foo extends Bar {

    private boolean someBoolean = true;

    private String someStringField;

    private float someExcludedField;

}

Colored by Color Scripter

cs

 

 

Java Code

1

2

3

4

5

6

7

8

9

10

11

12

public class Foo extends Bar {

    private boolean someBoolean = true;

    private String someStringField;

    private float someExcludedField;

    

    @java.lang.Override

    public java.lang.String toString() {

        return "Foo(super=" + super.toString() +

            ", someBoolean=" + someBoolean +

            ", someStringField=" + someStringField + ")";

    }

}

Colored by Color Scripter

cs

 

 

@EqualsAndHashCode

@EqualsAndHashCode은 코드에서 객체 비교 등의 용도로 사용되는 equals(), hashCode() 메소드의 코드를 생성해준다. 그리고 exclude={field-name,..}을 통해 특정 필드를 제외할 수도 있다.

 

Lombok Code

1

2

3

4

5

6

7

8

9

10

11

12

13

@EqualsAndHashCode(callSuper=true,exclude={"address","city","state","zip"})

public class Person extends SentientBeing {

    enum Gender { Male, Female }

 

    @NonNull private String name;

    @NonNull private Gender gender;

    

    private String ssn;

    private String address;

    private String city;

    private String state;

    private String zip;

}

Colored by Color Scripter

cs

 

 

Java Code

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

public class Person extends SentientBeing {

    

    enum Gender {

        /*public static final*/ Male /* = new Gender() */,

        /*public static final*/ Female /* = new Gender() */;

    }

    @NonNull

    private String name;

    @NonNull

    private Gender gender;

    private String ssn;

    private String address;

    private String city;

    private String state;

    private String zip;

    

    @java.lang.Override

    public boolean equals(final java.lang.Object o) {

        if (o == thisreturn true;

        if (o == nullreturn false;

        if (o.getClass() != this.getClass()) return false;

        if (!super.equals(o)) return false;

        final Person other = (Person)o;

        if (this.name == null ? other.name != null : !this.name.equals(other.name)) return false;

        if (this.gender == null ? other.gender != null : !this.gender.equals(other.gender)) return false;

        if (this.ssn == null ? other.ssn != null : !this.ssn.equals(other.ssn)) return false;

        return true;

    }

    

    @java.lang.Override

    public int hashCode() {

        final int PRIME = 31;

        int result = 1;

        result = result * PRIME + super.hashCode();

        result = result * PRIME + (this.name == null ? this.name.hashCode());

        result = result * PRIME + (this.gender == null ? this.gender.hashCode());

        result = result * PRIME + (this.ssn == null ? this.ssn.hashCode());

        return result;

    }

}

Colored by Color Scripter

cs

 

 

 

@Data

@DataClass에 정의된 모든 필드에 대한 getter, setter toString, equals, hashCode, final로 지정됐거나 @NonNull로 명시된 필드에 대한 값을 받는 생성자 메소드 코드를 생성해 준다. , 앞에서 배웠던 @Getter, @Setter, @NonNull, @EqualsAndHashCode, @ToString 에 대한 걸 모두 해주는 Annotation이다.

 

Lombok Code

1

2

3

4

5

6

@Data(staticConstructor="of")

public class Company {

    private final Person founder;

    private String name;

    private List<Person> employees;

}

Colored by Color Scripter

cs

 

 

Java Code

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

public class Company {

    private final Person founder;

    private String name;

    private List<Person> employees;

    

    private Company(final Person founder) {

        this.founder = founder;

    }

    

    public static Company of(final Person founder) {

        return new Company(founder);

    }

    

    public Person getFounder() {

        return founder;

    }

    

    public String getName() {

        return name;

    }

    

    public void setName(final String name) {

        this.name = name;

    }

    

    public List<Person> getEmployees() {

        return employees;

    }

    

    public void setEmployees(final List<Person> employees) {

        this.employees = employees;

    }

    

    @java.lang.Override

    public boolean equals(final java.lang.Object o) {

        if (o == thisreturn true;

        if (o == nullreturn false;

        if (o.getClass() != this.getClass()) return false;

        final Company other = (Company)o;

        if (this.founder == null ? other.founder != null : !this.founder.equals(other.founder)) return false;

        if (this.name == null ? other.name != null : !this.name.equals(other.name)) return false;

        if (this.employees == null ? other.employees != null : !this.employees.equals(other.employees)) return false;

        return true;

    }

    

    @java.lang.Override

    public int hashCode() {

        final int PRIME = 31;

        int result = 1;

        result = result * PRIME + (this.founder == null ? this.founder.hashCode());

        result = result * PRIME + (this.name == null ? this.name.hashCode());

        result = result * PRIME + (this.employees == null ? this.employees.hashCode());

        return result;

    }

    

    @java.lang.Override

    public java.lang.String toString() {

        return "Company(founder=" + founder + ", name=" + name + ", employees=" + employees + ")";

    }

}

Colored by Color Scripter

cs

 

 

 

 

@Value
@ValueImmutable Class을 생성해준다.  @Data와 비슷하지만 모든 필드를 기본적으로 Private Final로 로 하고, Setter 함수를 생성하지 않고, Class또한 Final로 지정하는 것만 빼고 동일하다.

 

Lombok Code

1

2

3

4

5

6

7

8

9

10

11

12

13

@Value public class ValueExample {

   String name;

   @Wither(AccessLevel.PACKAGE) @NonFinal int age;

   double score;

   protected String[] tags;

   

   @ToString(includeFieldNames=true)

   @Value(staticConstructor="of")

   public static class Exercise<T> {

     String name;

     T value;

   }

}

Colored by Color Scripter

cs

 

Java Code

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

public final class ValueExample {

   private final String name;

   private int age;

   private final double score;

   protected final String[] tags;

   

   @java.beans.ConstructorProperties({"name""age""score""tags"})

   public ValueExample(String name, int age, double score, String[] tags) {

     this.name = name;

     this.age = age;

     this.score = score;

     this.tags = tags;

   }

   

   public String getName() {

     return this.name;

   }

   

   public int getAge() {

     return this.age;

   }

   

   public double getScore() {

     return this.score;

   }

   

   public String[] getTags() {

     return this.tags;

   }

   

   @java.lang.Override

   public boolean equals(Object o) {

     if (o == thisreturn true;

     if (!(o instanceof ValueExample)) return false;

     final ValueExample other = (ValueExample)o;

     final Object this$name = this.getName();

     final Object other$name = other.getName();

     if (this$name == null ? other$name != null : !this$name.equals(other$name)) return false;

     if (this.getAge() != other.getAge()) return false;

     if (Double.compare(this.getScore(), other.getScore()) != 0return false;

     if (!Arrays.deepEquals(this.getTags(), other.getTags())) return false;

     return true;

   }

   

   @java.lang.Override

   public int hashCode() {

     final int PRIME = 59;

     int result = 1;

     final Object $name = this.getName();

     result = result * PRIME + ($name == null ? 43 : $name.hashCode());

     result = result * PRIME + this.getAge();

     final long $score = Double.doubleToLongBits(this.getScore());

     result = result * PRIME + (int)($score >>> 32 ^ $score);

     result = result * PRIME + Arrays.deepHashCode(this.getTags());

     return result;

   }

   

   @java.lang.Override

   public String toString() {

     return "ValueExample(name=" + getName() + ", age=" + getAge() + ", score=" + getScore() + ", tags=" + Arrays.deepToString(getTags()) + ")";

   }

   

   ValueExample withAge(int age) {

     return this.age == age ? this : new ValueExample(name, age, score, tags);

   }

   

   public static final class Exercise<T> {

     private final String name;

     private final T value;

     

     private Exercise(String name, T value) {

       this.name = name;

       this.value = value;

     }

     

     public static <T> Exercise<T> of(String name, T value) {

       return new Exercise<T>(name, value);

     }

     

     public String getName() {

       return this.name;

     }

     

     public T getValue() {

       return this.value;

     }

     

     @java.lang.Override

     public boolean equals(Object o) {

       if (o == thisreturn true;

       if (!(o instanceof ValueExample.Exercise)) return false;

       final Exercise<?> other = (Exercise<?>)o;

       final Object this$name = this.getName();

       final Object other$name = other.getName();

       if (this$name == null ? other$name != null : !this$name.equals(other$name)) return false;

       final Object this$value = this.getValue();

       final Object other$value = other.getValue();

       if (this$value == null ? other$value != null : !this$value.equals(other$value)) return false;

       return true;

     }

     

     @java.lang.Override

     public int hashCode() {

       final int PRIME = 59;

       int result = 1;

       final Object $name = this.getName();

       result = result * PRIME + ($name == null ? 43 : $name.hashCode());

       final Object $value = this.getValue();

       result = result * PRIME + ($value == null ? 43 : $value.hashCode());

       return result;

     }

     

     @java.lang.Override

     public String toString() {

       return "ValueExample.Exercise(name=" + getName() + ", value=" + getValue() + ")";

     }

   }

}

Colored by Color Scripter

cs

 

 

@Cleanup

@Cleanup Local 변수에 붙이면 Cleanup code 가 현재 code 가 종료될 때 자동으로 호출해주도록 한다. 그리고 close() method가 있으면 해당 변수 scope 끝까지 자동으로 try { } 로 묶어주고 finally 블록에서 field.close() 를 넣어준다. 이것은 컴파일 타임에 close()를 호출하도록 하는 것이므로 별도의 interface가 필요로 하지 않는다.

close() 가 없고 다른 method cleanup 을 수행할 경우 @Cleanup("dispose") 같이 method명을 작성해주면 해당 메소드를 호출해준다. , 만약 destroy() 을 불러야 하는 경우라면 @Cleanup("destroy")라고 작성해 주면 된다. , try 블록에서 예외가 발생했고 cleanup 메서드에서도 예외가 발생할 경우 cleanup 메서드에서 발생한 예외는 완전히 무시된다. 그러므로 @Cleanup 완전히 의존하는 것은 위험하다.

 

 

Lombok Code

1

2

3

4

5

6

7

8

9

public void testCleanUp() {

    try {

        @Cleanup ByteArrayOutputStream baos = new ByteArrayOutputStream();

        baos.write(new byte[] {'Y','e','s'});

        System.out.println(baos.toString());

    } catch (IOException e) {

        e.printStackTrace();

    }

}

Colored by Color Scripter

cs

 

 

Java Code

1

2

3

4

5

6

7

8

9

10

11

12

13

public void testCleanUp() {

    try {

        ByteArrayOutputStream baos = new ByteArrayOutputStream();

        try {

            baos.write(new byte[]{'Y''e''s'});

            System.out.println(baos.toString());

        } finally {

            baos.close();

        }

    } catch (IOException e) {

        e.printStackTrace();

    }

}

Colored by Color Scripter

cs

 

 

 

@Synchronized

@Synchronizedstatic instance lock 오브젝트를 자동으로 생성해주고, Annotation을 메서드에 적용할 경우에는 메서드 Body synchronized 키워드로 감싸준다.

여기서 관심있게 볼 부분은 new Object() serialize 되지 않는 것을 고려하여 $lock, instance용 오브젝트를 new Object[0]로 선언해준다는 것이다. Annotation된 메소드가 정적이면 Lombok을 통해 $LOCK, Class 오브젝트가 자동으로 작성되어 @Synchronized 메소드와 동기화시킨다.

만약 별도의 lock 객체를 생성해서 특정 메서드에만 적용하고 싶다면 (read-lock 같은 경우를 위해) @Synchronized("lock-object-name") 형식으로 쓰면 된다. 대신 lock-object-name은 프로그래머가 직접 필드를 선언해야만 한다. 예를 들면, @Synchronized("myObject") myObject오브젝트와 대조하여 @Synchronized 메소드를 동기화한다.

 

 

 

Lombok Code

1

2

3

4

5

6

private DateFormat format = new SimpleDateFormat("MM-dd-YYYY");

 

@Synchronized

public String synchronizedFormat(Date date) {

    return format.format(date);

}

Colored by Color Scripter

cs

 

 

Java Code

1

2

3

4

5

6

7

8

private final java.lang.Object $lock = new java.lang.Object[0];

private DateFormat format = new SimpleDateFormat("MM-dd-YYYY");

 

public String synchronizedFormat(Date date) {

    synchronized ($lock) {

        return format.format(date);

    }

}

Colored by Color Scripter

cs

 

 

@SneakyThrows
잘 일어나지 않는 checked exceptions을 처리해준다. 물론 IDE을 통해 checked exception Quick-Fix로 고치면 자동으로 e.printStackTrace()을 붙여준다.

 

 

Lombok Code

1

2

3

4

@SneakyThrows

public void testSneakyThrows() {

    throw new IllegalAccessException();

}

Colored by Color Scripter

cs

 

 

Java Code

1

2

3

4

5

6

7

public void testSneakyThrows() {

    try {

        throw new IllegalAccessException();

    } catch (java.lang.Throwable $ex) {

        throw lombok.Lombok.sneakyThrow($ex);

    }

}

Colored by Color Scripter

cs

 

val

Local 변수 선언을 val을 이용해서 하면 초기화 값을 이용하여 변수 타입을 유추해서 final로 생성해 준다. 즉 자바스크립트에서 var 키워드와 동일한 기능을 한다고 생각하면 된다.

 

Lombok Code

1

2

3

4

5

6

7

8

9

10

11

12

13

14

public String example() {

    val example = new ArrayList<String>();

  example.add("Hello, World!");

  val foo = example.get(0);

  return foo.toLowerCase();

}

public void example2() {

  val map = new HashMap<Integer, String>();

  map.put(0"zero");

  map.put(5"five");

  for (val entry : map.entrySet()) {

    System.out.printf("%d: %s\n", entry.getKey(), entry.getValue());

  }

}

Colored by Color Scripter

cs

 

 

Java Code

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

public String example() {

     final ArrayList<String> example = new ArrayList<String>();

     example.add("Hello, World!");

     final String foo = example.get(0);

     return foo.toLowerCase();

}

   

public void example2() {

     final HashMap<Integer, String> map = new HashMap<Integer, String>();

     map.put(0"zero");

     map.put(5"five");

     for (final Map.Entry<Integer, String> entry : map.entrySet()) {

       System.out.printf("%d: %s\n", entry.getKey(), entry.getValue());

     }

}

Colored by Color Scripter

cs

 

 

 

 

@Log

자동으로 logging을 위한 필드인 private static final Logger log 가 추가됩니다. 이후 로그를 출력하려는 곳에서 log.error(), log.warn(), log.debug(), log.info() 형태로 사용하면 된다.

이외에도 다양한 로그 타입을 지원한다. (@CommonsLog, @JBossLog, @Log4j, @Log4j2, @Slf4j, @XSlf4j)

 

Lombok Code

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

@Log

public class LogExample {

   public static void main(String... args) {

     log.error("Something's wrong here");

   }

}

 

@Slf4j

public class LogExampleOther {

   public static void main(String... args) {

     log.error("Something else is wrong here");

   }

}

 

@CommonsLog(topic="CounterLog")

public class LogExampleCategory {

   public static void main(String... args) {

     log.error("Calling the 'CounterLog' with a message");

   }

}

Colored by Color Scripter

cs

 

 

Java Code

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

public class LogExample {

   private static final java.util.logging.Logger log = java.util.logging.Logger.getLogger(LogExample.class.getName());

   

   public static void main(String... args) {

     log.error("Something's wrong here");

   }

}

 

public class LogExampleOther {

   private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(LogExampleOther.class);

   

   public static void main(String... args) {

     log.error("Something else is wrong here");

   }

}

 

public class LogExampleCategory {

   private static final org.apache.commons.logging.Log log = org.apache.commons.logging.LogFactory.getLog("CounterLog");

 

   public static void main(String... args) {

     log.error("Calling the 'CounterLog' with a message");

   }

}

Colored by Color Scripter

cs

 

 

@Builder
@Builder AnnotationClass에 대한 복잡한 Builder API들을 자동으로 생성해 준다. 그리고 @BuilderClass 뿐만 아니라, 생성자, 메서드에도 사용할 수 있다. 추가적으로 @Singular Annotation도 제공한다. @Singular@ Builder Annotation을 사용한 Class/Method/Constructor에서 Collection 객체에만 사용할 수 있다. Collection 변수에 @Singular을 설정하면 setter 함수 대신 두 종류의 Adder 함수와 Clear함수를 생성해 준다. Adder 함수는 1개 또는 모두 요소를 추가하는 두 종류의 Adder 함수가 생성된다. 그리고 Collectionclear 하는 함수도 생성해준다. 현재 Lombok에서 지원하는 Collectionjava.utilGuava(com.google.common.collect) 만을 지원한다.

 

 

Lombok Code

1

2

3

4

5

6

@Builder

public class BuilderExample {

   private String name;

   private int age;

   @Singular private Set<String> occupations;

}

Colored by Color Scripter

cs

 

 

Java Code

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

public class BuilderExample {

   private String name;

   private int age;

   private Set<String> occupations;

   

   BuilderExample(String name, int age, Set<String> occupations) {

     this.name = name;

     this.age = age;

     this.occupations = occupations;

   }

   

   public static BuilderExampleBuilder builder() {

     return new BuilderExampleBuilder();

   }

   

   public static class BuilderExampleBuilder {

     private String name;

     private int age;

     private java.util.ArrayList<String> occupations;

     

     BuilderExampleBuilder() {

     }

     

     public BuilderExampleBuilder name(String name) {

       this.name = name;

       return this;

     }

     

     public BuilderExampleBuilder age(int age) {

       this.age = age;

       return this;

     }

     

     public BuilderExampleBuilder occupation(String occupation) {

       if (this.occupations == null) {

         this.occupations = new java.util.ArrayList<String>();

       }

       

       this.occupations.add(occupation);

       return this;

     }

     

     public BuilderExampleBuilder occupations(Collection<extends String> occupations) {

       if (this.occupations == null) {

         this.occupations = new java.util.ArrayList<String>();

       }

 

       this.occupations.addAll(occupations);

       return this;

     }

     

     public BuilderExampleBuilder clearOccupations() {

       if (this.occupations != null) {

         this.occupations.clear();

       }

       

       return this;

     }

 

     public BuilderExample build() {

       // complicated switch statement to produce a compact properly sized immutable set omitted.

       // go to https://projectlombok.org/features/Singular-snippet.html to see it.

       Set<String> occupations = ...;

       return new BuilderExample(name, age, occupations);

    }

     

     @java.lang.Override

     public String toString() {

       return "BuilderExample.BuilderExampleBuilder(name = " + this.name + ", age = " + this.age + ", occupations = " + this.occupations + ")";

     }

   }

}

Colored by Color Scripter

cs

 

 

그 이외에 Lombok의 공식적인 Feature은 아니지만 테스트 중인 다양한 feature들도 있다.

 

참고 사이트

 

https://projectlombok.org/features/

http://jnb.ociweb.com/jnb/jnbJan2010.html

https://blogs.idincu.com/dev/?p=17

http://edoli.tistory.com/99

http://kwonnam.pe.kr/wiki/java/lombok?s[]=lombok

 

'IT > Lombok' 카테고리의 다른 글

LOMBOK 이란  (0) 2017.01.06

+ Recent posts