Java開發利器Lombok
-
Lombok簡介
Lombok是一種Java的實用工具,可用來幫助開發人員消除Java的冗長程式碼,尤其是對於簡單的Java物件(POJO)。它通過註釋實現這一目的。通過在開發環境中實現Lombok,開發人員可以節省構建getter/setter以及諸如hashCode()和equals()這樣的方法。Lombok會在編譯時修改插入程式碼,因此不會影響任何執行時的效能。
-
安裝Lombok
<dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.16.20</version> <scope>provided</scope> </dependency>
Jetbrains IntelliJ IDEA使用者需要安裝外掛
-
Lombok用法
-
@Data註解
@Data直接修飾POJO或者beans, getter所有的變數,setter所有不為final的變數。如果不需要預設的生成方式,直接填寫你需要的annotation的就可以了。預設生成的所有的annotation都是public的,如果需要不同許可權修飾符可以使用AccessLevel選項。當然@Data 也可以使用staticConstructor選項生成一個靜態方法。包含了以下幾種註解:
註解 | 生成 | 選項 |
---|---|---|
@Getter / @Setter | getter / setter 方法 | / |
@ToString | toString方法 | includeFieldNames,toString方法是否輸出成員變數名,可選值true/false,預設為true |
@EqualsAndHashCode | equals()和hashCode()方法 | / |
@RequiredArgsConstructor | 包含非空引數(未初始化的final成員變數或者@NonNull成員變數)的構造器 | / |
POJO添加了@Data註解,相當於添加了上述五個註解,示例程式碼:
@Data public class DataExample { private final String name; private final String name1 = "zhuoli"; @NonNull private String name2; @NonNull private String name3 = "zhuoli"; @Setter(AccessLevel.PACKAGE) private int age; private double score; private String[] tags; @ToString(includeFieldNames=false) @Data(staticConstructor="of") public static class Exercise<T> { private final String name; private final T value; } }
編譯後生成的class檔案:
public class DataExample {
private final String name;
private final String name1 = "zhuoli";
@NonNull
private String name2;
@NonNull
private String name3 = "zhuoli";
private int age;
private double score;
private String[] tags;
public DataExample(String name, @NonNull String name2) {
if (name2 == null) {
throw new NullPointerException("name2");
} else {
this.name = name;
this.name2 = name2;
}
}
public String getName() {
return this.name;
}
public String getName1() {
this.getClass();
return "zhuoli";
}
@NonNull
public String getName2() {
return this.name2;
}
@NonNull
public String getName3() {
return this.name3;
}
public int getAge() {
return this.age;
}
public double getScore() {
return this.score;
}
public String[] getTags() {
return this.tags;
}
public void setName2(@NonNull String name2) {
if (name2 == null) {
throw new NullPointerException("name2");
} else {
this.name2 = name2;
}
}
public void setName3(@NonNull String name3) {
if (name3 == null) {
throw new NullPointerException("name3");
} else {
this.name3 = name3;
}
}
public void setScore(double score) {
this.score = score;
}
public void setTags(String[] tags) {
this.tags = tags;
}
public boolean equals(Object o) {
//省略
}
protected boolean canEqual(Object other) {
return other instanceof DataExample;
}
public int hashCode() {
}
public String toString() {
return "DataExample(name=" + this.getName() + ", name1=" + this.getName1() + ", name2=" + this.getName2() + ", name3=" + this.getName3() + ", age=" + this.getAge() + ", score=" + this.getScore() + ", tags=" + Arrays.deepToString(this.getTags()) + ")";
}
void setAge(int age) {
this.age = age;
}
public static class Exercise<T> {
private final String name;
private final T value;
public String toString() {
return "DataExample.Exercise(" + this.getName() + ", " + this.getValue() + ")";
}
private Exercise(String name, T value) {
this.name = name;
this.value = value;
}
public static <T> DataExample.Exercise<T> of(String name, T value) {
return new DataExample.Exercise(name, value);
}
public String getName() {
return this.name;
}
public T getValue() {
return this.value;
}
public boolean equals(Object o) {
//省略
}
protected boolean canEqual(Object other) {
return other instanceof DataExample.Exercise;
}
public int hashCode() {
//省略
}
}
}
值得注意的是name1和name3因為已經初始化,生成的建構函式引數中不包含這兩項,@Setter使用了AccessLevel.PACKAGE選項,所以生成的setter方法美加許可權修飾符,為包訪問許可權。Exercise內部@ToString註解添加了includeFieldNames=false選項,所以生成的toString方法不包含變數名,@Data添加了staticConstructor=”of”屬性,所以生成了靜態建構函式of。
-
@Value註解
@Value與@Data的主要區別就是,如果成員變數不加@NonFinal ,編譯後生成的類所有的成員變數都會是final的,相應的,就沒有set方法了,示例程式碼:
@Value
public class ValueExample {
private String name;
@Wither(AccessLevel.PACKAGE)
@NonFinal
private int age;
private double score;
protected String[] tags;
@ToString
@Value(staticConstructor="of")
public static class Exercise<T> {
String name;
T value;
}
}
編譯後生成的class檔案:
public final class ValueExample {
private final String name;
private int age;
private final double score;
protected final String[] 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;
}
public boolean equals(Object o) {
//省略
}
public int hashCode() {
//省略
}
public String toString() {
return "ValueExample(name=" + this.getName() + ", age=" + this.getAge() + ", score=" + this.getScore() + ", tags=" + Arrays.deepToString(this.getTags()) + ")";
}
ValueExample withAge(int age) {
return this.age == age ? this : new ValueExample(this.name, age, this.score, this.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> ValueExample.Exercise<T> of(String name, T value) {
return new ValueExample.Exercise(name, value);
}
public String getName() {
return this.name;
}
public T getValue() {
return this.value;
}
public boolean equals(Object o) {
//省略
}
public int hashCode() {
//省略
}
public String toString() {
return "ValueExample.Exercise(name=" + this.getName() + ", value=" + this.getValue() + ")";
}
}
}
可以看到,除了@NonFinal標註的成員變數age,其他成員變數編譯後都變成final,但是age成員變量出現在了建構函式中(這一點跟@RequiredArgsConstructor註解有點違背,我也每太看懂)。同時生成了一個包訪問許可權的WithAge方法,返回一個類例項,不多說,看一下程式碼就能看懂
-
@Builder註解
@Builder註解標註在類、構造器或方法上,可生成各種builder APIs,允許開發者以如下方式呼叫:
Person.builder().name("Adam Savage").city("San Francisco").job("Mythbusters").job("Unchained Reaction").build();
@Builder
@ToString
@Getter
public class BuilderExample {
/*如果created沒有被build賦值,則取預設值*/
@Builder.Default
private long created = System.currentTimeMillis();
private String name;
private int age;
/*表明成員變數是collection*/
@Singular
private Set<String> occupations;
}
呼叫示例:
invoke com.pdd.service.ad.biz.common.contract.service.AccountBalanceRemindingService.createBalanceRemindingRecord({"mallId": 565656, "remindValue": 9988, "mobile": "13156897423", "isMsgboxRemind": 1, "isSmsRemind": 1, "isAutoCharge": 1, "singleChargeAmount": 100000})
ad-biz-integration-api.finance.contract.appkey
ad-biz-integration-api.finance.contract.secret
invoke com.pdd.service.ad.biz.common.contract.service.AccountBalanceRemindingService.updateBalanceRemindingRecord({"mallId":236639, "isAutoCharge":1, "singleChargeAmount":50000, "dayChargeAmount":100000})
I2ARedisValueV6
I2ARedisValueV6
DataExample(name=" + this.getName() + ", name1=" + this.getName1() + ", name2=" + this.getName2() + ", name3=" + this.getName3() + ", age=" + this.getAge() + ", score=" + this.getScore() + ", tags=" + Arrays.deepToString(this.getTags()) + ")
DataExample.Exercise(name=" + this.getName() + ", value=" + this.getValue() + ")"
@Test
public void builderTest(){
BuilderExample builderExample = BuilderExample.builder().name("zhuoli").age(22).occupation("haha").build();
assertThat(builderExample.getOccupations(), containsInAnyOrder("haha"));
BuilderExample builderExample1 = BuilderExample.builder().occupations(Sets.newHashSet("this", "is", "builder")).build();
assertThat(builderExample1.getOccupations(), containsInAnyOrder("this", "is", "builder"));
}
-
@Log註解
@Log註解標註在類上,並根據不同的註解生成不同型別的日誌logger物件,但是例項名稱都是log,修飾屬性都是private static final,目前支援以下幾種logger:
註解 | 生成物件 |
---|---|
@CommonsLog | org.apache.commons.logging.Log |
@JBossLog | org.jboss.logging.Logger |
@Log | java.util.logging.Logger |
@Log4j | org.apache.log4j.Logger |
@Log4j2 | org.apache.log4j.LogManager |
@Slf4j | org.slf4j.Logger |
@XSlf4j | org.slf4j.ext.XLogger |
如@CommonsLog相當於:
private static final org.apache.commons.logging.Log log = org.apache.commons.logging.LogFactory.getLog(LogExample.class);
@Slf4j相當於:
private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(LogExample.class);
更多細節,請參考官方文件
-
@XXXConstructor註解
該系列註解提供自動生成建構函式的功能,主要分為以下三類:
註解 | 生成構造器 |
---|---|
@NoArgsConstructor | 無參構造器 |
@RequiredArgsConstructor | 包含非空引數(未初始化的final成員變數或者@NonNull成員變數)的構造器 |
@AllArgsConstructor | 包含所有非static成員、未初始化的final成員的構造器 |
@AllArgsConstructor
public class AllArgsConstructorExample {
private Integer x;
private Integer y;
private static Integer z;
private final Integer z1;
private final Integer z2 = 2;
private static final Integer z3 = 1;
}
編譯後生成的程式碼:
public class AllArgsConstructorExample {
private Integer x;
private Integer y;
private static Integer z;
private final Integer z1;
private final Integer z2 = 2;
private static final Integer z3 = 1;
public AllArgsConstructorExample(Integer x, Integer y, Integer z1) {
this.x = x;
this.y = y;
this.z1 = z1;
}
}
static成員變數z、z3,已初始化的成員變數z2沒有出現在建構函式中
-
@NonNull註解
@NonNull註解提供成員變數的非空檢查
public NonNullExample(@NonNull Person person) {
super("Hello");
this.name = person.getName();
}
// 等同於
public NonNullExample(Person person) {
super("Hello");
if (person == null) {
throw new NullPointerException("person");
}
this.name = person.getName();
}
-
@SneakyThrows註解
@SneakyThrows註解的方法會自動丟擲受檢異常,而無需顯式在方法上使用throws語句
-
@Synchronized註解
@Synchronized註解標註在方法上,將方法宣告為同步的,並自動加鎖,而鎖物件是一個私有的屬性$lock或$LOCK。示例程式碼:
public class SynchronizedExample {
private final Object readLock = new Object();
@Synchronized
public static void hello() {
System.out.println("world");
}
@Synchronized
public int answerToLife() {
return 42;
}
@Synchronized("readLock")
public void foo() {
System.out.println("bar");
}
}
// 等同於
public class SynchronizedExample {
private static final Object $LOCK = new Object[0];
private final Object $lock = new Object[0];
private final Object readLock = new Object();
public static void hello() {
synchronized($LOCK) {
System.out.println("world");
}
}
public int answerToLife() {
synchronized($lock) {
return 42;
}
}
public void foo() {
synchronized(readLock) {
System.out.println("bar");
}
}
}
- @Getter(lazy=true)註解
@Getter(lazy=true)註解可以替代經典的Double Check Lock樣板程式碼,實現單例,示例程式碼:
public class GetterLazyExample {
@Getter(lazy=true) private final double[] cached = expensive();
private double[] expensive() {
double[] result = new double[1000000];
for (int i = 0; i < result.length; i++) {
result[i] = Math.asin(i);
}
return result;
}
}
// 等同於
public class GetterLazyExample {
private final java.util.concurrent.AtomicReference<java.lang.Object> cached = new java.util.concurrent.AtomicReference<>();
public double[] getCached() {
java.lang.Object value = this.cached.get();
if (value == null) {
synchronized(this.cached) {
value = this.cached.get();
if (value == null) {
final double[] actualValue = expensive();
value = actualValue == null ? this.cached : actualValue;
this.cached.set(value);
}
}
}
return (double[])(value == this.cached ? null : value);
}
private double[] expensive() {
double[] result = new double[1000000];
for (int i = 0; i < result.length; i++) {
result[i] = Math.asin(i);
}
return result;
}
}