Project lombok автоматизирует написание одного и того же кода и позволяет сосредоточиться на задаче и при этом писать более простой и понятный код. С другой стороны, первый постулат Пардо напоминает: «Все, что есть хорошего в жизни, либо незаконно, либо аморально, либо ведет к ожирению». Какова цена удобства, которое привносит lombok?
В первую очередь я бы назвал недостатком lombok саму концепцию: использование аннотаций для генерации кода. Аннотации задумывались как средство задания свойства кода, которые могут изменять поведение кода, но не сам код. Project lombok использует их для генерации кода, что идеологически сомнительно.
Из этого недостатка вытекают другие — вся экосистема java не готова к project lombok. Расчёт покрытия unit тестами будет давать неверные оценки. Анализатор PMD будет ругаться на недоступные поля. FindBugs будет ругаться на некорректное исползьование методов (хотя с недавних пор это отключаемо). AspectJ вообще не способен собрать проект с lombok.
Что же делать? Есть ли возможность писать простой код с project lombok и при этом минимизировать ущерб от него? Авторы lombok называют эту возможность разломбочиванием «delomboking».
Проще всего, если эта операция не требуется часто, разломбочить вручную:
1
|
java -jar lombok delombok src -d src-delomboked
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
/**
* Sample account entity.
*/
@Data(staticConstructor = "create")
public class Account {
/**
* Id.
*/
@NonNull
Long id;
/**
* Account owner.
*/
@NonNull
User owner;
/**
* Account's value.
*/
BigDecimal amount;
}
|
Разломбоченный код настолько большой, что пришлось спрятать его.
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
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
|
// Generated by delombok at Mon Nov 09 13:30:44 EET 2015
package ru.easyjava.java.accounts;
import lombok.NonNull;
import ru.easyjava.java.users.User;
import java.math.BigDecimal;
/**
* Sample account entity.
*/
public class Account {
/**
* Id.
*/
@NonNull
Long id;
/**
* Account owner.
*/
@NonNull
User owner;
/**
* Account's value.
*/
BigDecimal amount;
@java.lang.SuppressWarnings("all")
@javax.annotation.Generated("lombok")
private Account(@NonNull final Long id, @NonNull final User owner) {
if (id == null) {
throw new java.lang.NullPointerException("id");
}
if (owner == null) {
throw new java.lang.NullPointerException("owner");
}
this.id = id;
this.owner = owner;
}
@java.lang.SuppressWarnings("all")
@javax.annotation.Generated("lombok")
public static Account create(@NonNull final Long id, @NonNull final User owner) {
return new Account(id, owner);
}
/**
* Id.
*/
@NonNull
@java.lang.SuppressWarnings("all")
@javax.annotation.Generated("lombok")
public Long getId() {
return this.id;
}
/**
* Account owner.
*/
@NonNull
@java.lang.SuppressWarnings("all")
@javax.annotation.Generated("lombok")
public User getOwner() {
return this.owner;
}
/**
* Account's value.
*/
@java.lang.SuppressWarnings("all")
@javax.annotation.Generated("lombok")
public BigDecimal getAmount() {
return this.amount;
}
/**
* Id.
*/
@java.lang.SuppressWarnings("all")
@javax.annotation.Generated("lombok")
public void setId(@NonNull final Long id) {
if (id == null) {
throw new java.lang.NullPointerException("id");
}
this.id = id;
}
/**
* Account owner.
*/
@java.lang.SuppressWarnings("all")
@javax.annotation.Generated("lombok")
public void setOwner(@NonNull final User owner) {
if (owner == null) {
throw new java.lang.NullPointerException("owner");
}
this.owner = owner;
}
/**
* Account's value.
*/
@java.lang.SuppressWarnings("all")
@javax.annotation.Generated("lombok")
public void setAmount(final BigDecimal amount) {
this.amount = amount;
}
@java.lang.Override
@java.lang.SuppressWarnings("all")
@javax.annotation.Generated("lombok")
public boolean equals(final java.lang.Object o) {
if (o == this) return true;
if (!(o instanceof Account)) return false;
final Account other = (Account)o;
if (!other.canEqual((java.lang.Object)this)) return false;
final java.lang.Object this$id = this.getId();
final java.lang.Object other$id = other.getId();
if (this$id == null ? other$id != null : !this$id.equals(other$id)) return false;
final java.lang.Object this$owner = this.getOwner();
final java.lang.Object other$owner = other.getOwner();
if (this$owner == null ? other$owner != null : !this$owner.equals(other$owner)) return false;
final java.lang.Object this$amount = this.getAmount();
final java.lang.Object other$amount = other.getAmount();
if (this$amount == null ? other$amount != null : !this$amount.equals(other$amount)) return false;
return true;
}
@java.lang.SuppressWarnings("all")
@javax.annotation.Generated("lombok")
protected boolean canEqual(final java.lang.Object other) {
return other instanceof Account;
}
@java.lang.Override
@java.lang.SuppressWarnings("all")
@javax.annotation.Generated("lombok")
public int hashCode() {
final int PRIME = 59;
int result = 1;
final java.lang.Object $id = this.getId();
result = result * PRIME + ($id == null ? 43 : $id.hashCode());
final java.lang.Object $owner = this.getOwner();
result = result * PRIME + ($owner == null ? 43 : $owner.hashCode());
final java.lang.Object $amount = this.getAmount();
result = result * PRIME + ($amount == null ? 43 : $amount.hashCode());
return result;
}
@java.lang.Override
@java.lang.SuppressWarnings("all")
@javax.annotation.Generated("lombok")
public java.lang.String toString() {
return "Account(id=" + this.getId() + ", owner=" + this.getOwner() + ", amount=" + this.getAmount() + ")";
}
}
|
Maven
Для maven существует особый плагин, интегрирующий разломбочивание в сборочный ковейер.
1
2
3
4
5
6
7
8
9
10
11
12
13
|
<plugin>
<groupId>org.projectlombok</groupId>
<artifactId>lombok-maven-plugin</artifactId>
<version>1.16.6.1</version>
<executions>
<execution>
<phase>generate-sources</phase>
<goals>
<goal>delombok</goal>
</goals>
</execution>
</executions>
</plugin>
|
Плагин ожидает, что весь код, требующий разломбочивания, будет лежать в src/main/lombok, а разломбоченный код будет помещён в target/generated-sources/delombok, откуда его уже подхватит компилятор и скомпилирует. Код, лежащий в src/main/java, будет скомпилирован как обычно.
Gradle
В gradle поддержка project lombok так же осуществляется отдельным плагином:
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
|
apply plugin: 'java'
buildscript {
repositories {
maven {
url "https://plugins.gradle.org/m2/"
}
}
dependencies {
classpath "gradle.plugin.net.franz-becker:gradle-lombok:1.5"
}
}
apply plugin: "net.franz-becker.gradle-lombok"
sourceCompatibility = 1.8
repositories {
mavenCentral()
}
dependencies {
testCompile group: 'junit', name: 'junit', version: '4.12'
testCompile group: 'org.hamcrest', name: 'hamcrest-core', version: '1.3'
}
/**
* Code below only required, if you need delomboking.
*/
import net.franz_becker.gradle.lombok.task.DelombokTask
task delombok(type: DelombokTask) {
args("src/main/lombok", "-d", "build/src/main/java")
}
task delombokHelp(type: DelombokTask) {
args "--help"
}
gradle.projectsEvaluated {
compileJava.dependsOn(delombok)
}
sourceSets {
main {
java {
srcDir 'build/src/main/java'
}
}
}
|
Поскольку в Gradle из коробки нет scope provided, как в maven, наличие плагина обязательно для корректной компиляции кода с lombok. Разломбочивание реализуется отдельным gradle task и требует дополнительной настройки (просто скопируйте task из примера :)).
Код примера доступен на github.