如何引入
maven工程,直接引入lombok的jar依賴即可:
<dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.16.18</version> </dependency>
val
我們定義一個常量都會這樣寫:
import java.util.ArrayList; import java.util.HashMap; import java.util.Map; public class ValExample { 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()); } } }
但是, 自從有了Lombok, final + 類型 這種復雜的定義方式不再需要了。
import java.util.ArrayList; import java.util.HashMap; import lombok.val; public class ValExample { 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()); } } }
@NotNull
對比下面兩端代碼:
import lombok.NonNull; public class NonNullExample extends Something { private String name; public NonNullExample(@NonNull Person person) { super("Hello"); this.name = person.getName(); } }
import lombok.NonNull; public class NonNullExample extends Something { private String name; public NonNullExample(Person person) { super("Hello"); if (person == null) { throw new NullPointerException("person"); } this.name = person.getName(); } }
估計看過guava代碼的應該都看到過這種annotation。其實NotNull包括了兩種Exception,有一個可選的annotation參數,通過exceptionType,可以決定當前位置是用IllegalArgumentException還是NPE,當然默認值就是NPE了。在Lombok中有一個好處就是如果使用了如@Data這樣的Lombok annotation,也會默認觸發@NotNull 的, 同時如果把@NotNull加到了原始類型上,你將收獲一個warning。
@Cleanup
使用前:
import java.io.*; public class CleanupExample { public static void main(String[] args) throws IOException { InputStream in = new FileInputStream(args[0]); try { OutputStream out = new FileOutputStream(args[1]); try { byte[] b = new byte[10000]; while (true) { int r = in.read(b); if (r == -1) break; out.write(b, 0, r); } } finally { if (out != null) { out.close(); } } } finally { if (in != null) { in.close(); } } } }
使用后:
import lombok.Cleanup; import java.io.*; public class CleanupExample { public static void main(String[] args) throws IOException { @Cleanup InputStream in = new FileInputStream(args[0]); @Cleanup OutputStream out = new FileOutputStream(args[1]); byte[] b = new byte[10000]; while (true) { int r = in.read(b); if (r == -1) break; out.write(b, 0, r); } } }
單單從代碼的行數上面就可以知道已經精簡了不少,同時,代碼的可讀性也進一步提高。從代碼中我們可以容易的看出,@Cleanup的作用就是在當前變量不在有效范圍內的時候,對其進行自動的資源回收。在Java中的Stream上使用Cleanup Annotation,就是對其調用close方法。
但是,請大家注意,Cleanup 存在一個小問題:
官網給出了提示,如果你的代碼中出現了異常,那么會觸發cleanup方法拋出異常,導致把原始異常吞掉,這樣就導致你在上層不知道發生了什么事情,這個事情很嚴重啊,但是如果說你是在調用close方法的時候出了異常,那么Cleanup這個annotation是不會把異常吞掉的。 同時,官網也指出,作者也沒有找到更好的方式去解決這個問題,如果他們找到了會立刻fix這個問題,否則也就只能等待Java 有可能在Update的時候出現新的解決方案。
@Getter, @Setter
想必你一定為寫這樣的代碼而苦惱:
public class GetterSetterExample { private int age = 10; private String name; @Override public String toString() { return String.format("%s (age: %d)", name, age); } public int getAge() { return age; } public void setAge(int age) { this.age = age; } protected void setName(String name) { this.name = name; } }
那么這兩個annotation到底能干什么呢?
import lombok.AccessLevel; import lombok.Getter; import lombok.Setter; public class GetterSetterExample { @Getter @Setter private int age = 10; @Setter(AccessLevel.PROTECTED) private String name; @Override public String toString() { return String.format("%s (age: %d)", name, age); } }
如果沒有設置AccessLevel屬性,則生成的默認方法為public的。如果想要使get,set方法不生效,則可以使用一個特殊的AccessLevel 選項 “NONE”。
同樣給大家一點小的建議:
在使用的時候如果當前bean中包括某個方法,恰好要和生成的方法名字一樣,則Lombok不會再處理生成,無論是不是兩個函數的方法是重載。例如:如果類中包含String get(String anme), 則不會再為bean生成一個新的方法。
@ToString
我們的代碼經常這樣:
import java.util.Arrays; public class ToStringExample { private static final int STATIC_VAR = 10; private String name; private Shape shape = new Square(5, 10); private String[] tags; private int id; public String getName() { return this.getName(); } public static class Square extends Shape { private final int width, height; public Square(int width, int height) { this.width = width; this.height = height; } @Override public String toString() { return "Square(super=" + super.toString() + ", width=" + this.width + ", height=" + this.height + ")"; } } @Override public String toString() { return "ToStringExample(" + this.getName() + ", " + this.shape + ", " + Arrays.deepToString(this.tags) + ")"; } }
其實,我們的代碼可以這樣:
import lombok.ToString; @ToString(exclude="id") public class ToStringExample { private static final int STATIC_VAR = 10; private String name; private Shape shape = new Square(5, 10); private String[] tags; private int id; public String getName() { return this.getName(); } @ToString(callSuper=true, includeFieldNames=true) public static class Square extends Shape { private final int width, height; public Square(int width, int height) { this.width = width; this.height = height; } } }
這樣的方便方式節省了好多拼接字符串的功夫。
lombok.toString.includeFieldNames
= [true
| false
] (default: true)是否包含field的信息,如果值為true ,則可以在toString方法中給出field 的name。
lombok.toString.doNotUseGetters
= [true
| false
] (default: false)如果值為true,則Lombok會直接獲取field 而不是通過get方法獲取值。
@EqualsAndHashCode
實現equals, hashCode方法是在編程生活中再常見不過的一個東西了,那么自然@EqualsAndHashCode 這個annotation就成為了一個非常方便的工具。默認情況下,被這個annotation標注的class會用到除了 static,transient修飾的所有屬性作為判斷標准,當然和之前的annotation一樣,可是使用exclude選項除掉不想要的屬性。也可以通過callSuper包含父類的equals 和 hashCode。 當然如果你的class 沒有繼承任何其他的class,你卻寫了callSuper,那么會收獲一個編譯報錯。
你只要這樣用:
import lombok.EqualsAndHashCode; @EqualsAndHashCode(exclude={"id", "shape"}) public class EqualsAndHashCodeExample { private transient int transientVar = 10; private String name; private double score; private Shape shape = new Square(5, 10); private String[] tags; private int id; public String getName() { return this.name; } @EqualsAndHashCode(callSuper=true) public static class Square extends Shape { private final int width, height; public Square(int width, int height) { this.width = width; this.height = height; } } }
就可以得到這樣的效果:
import java.util.Arrays; public class EqualsAndHashCodeExample { private transient int transientVar = 10; private String name; private double score; private Shape shape = new Square(5, 10); private String[] tags; private int id; public String getName() { return this.name; } @Override public boolean equals(Object o) { if (o == this) return true; if (!(o instanceof EqualsAndHashCodeExample)) return false; EqualsAndHashCodeExample other = (EqualsAndHashCodeExample) o; if (!other.canEqual((Object)this)) return false; if (this.getName() == null ? other.getName() != null : !this.getName().equals(other.getName())) return false; if (Double.compare(this.score, other.score) != 0) return false; if (!Arrays.deepEquals(this.tags, other.tags)) return false; return true; } @Override public int hashCode() { final int PRIME = 59; int result = 1; final long temp1 = Double.doubleToLongBits(this.score); result = (result*PRIME) + (this.name == null ? 0 : this.name.hashCode()); result = (result*PRIME) + (int)(temp1 ^ (temp1 >>> 32)); result = (result*PRIME) + Arrays.deepHashCode(this.tags); return result; } protected boolean canEqual(Object other) { return other instanceof EqualsAndHashCodeExample; } public static class Square extends Shape { private final int width, height; public Square(int width, int height) { this.width = width; this.height = height; } @Override public boolean equals(Object o) { if (o == this) return true; if (!(o instanceof Square)) return false; Square other = (Square) o; if (!other.canEqual((Object)this)) return false; if (!super.equals(o)) return false; if (this.width != other.width) return false; if (this.height != other.height) return false; return true; } @Override public int hashCode() { final int PRIME = 59; int result = 1; result = (result*PRIME) + super.hashCode(); result = (result*PRIME) + this.width; result = (result*PRIME) + this.height; return result; } protected boolean canEqual(Object other) { return other instanceof Square; } } }
這就是Lombok的優勢所在。幾個需要注意的點是,對於Array類型的變量,如果需要進行equals和hashCode的時候需要調用工具類的Arrays中的deepHashCode, deepEquals完成比較。
Constructor
在Lombok中,生成構造方法的annotation一共有三個,@NoArgsConstructor, @RequiredArgsConstructor, @AllArgsContructor。使用這三個annotation來完成項目中對於不同構造方法的需求。
@NoArgsConstructor : 生成一個無參數的構造方法,這個annotation在與其他的annotation配合起來使用的時候更加能凸顯出他的重要性,例如在使用hibernate這種框架的時候,如果有一個有參數的構造方法的時候,NoArgsConstructor會展示出他的作用。
@RequiredArgsConstructor: 會生成一個包含常量,和標識了NotNull的變量 的構造方法。生成的構造方法是private,如何想要對外提供使用可以使用staticName選項生成一個static方法。
@AllArgsContructor: 會生成一個包含所有變量,同時如果變量使用了NotNull annotation , 會進行是否為空的校驗。
我們來看一下官方給出的一個例子:
import lombok.AccessLevel; import lombok.RequiredArgsConstructor; import lombok.AllArgsConstructor; import lombok.NonNull; @RequiredArgsConstructor(staticName = "of") @AllArgsConstructor(access = AccessLevel.PROTECTED) public class ConstructorExample<T> { private int x, y; @NonNull private T description; @NoArgsConstructor public static class NoArgsExample { @NonNull private String field; } }
上面的例子用Java代碼翻譯一下就是:
public class ConstructorExample<T> { private int x, y; @NonNull private T description; private ConstructorExample(T description) { if (description == null) throw new NullPointerException("description"); this.description = description; } public static <T> ConstructorExample<T> of(T description) { return new ConstructorExample<T>(description); } @java.beans.ConstructorProperties({"x", "y", "description"}) protected ConstructorExample(int x, int y, T description) { if (description == null) throw new NullPointerException("description"); this.x = x; this.y = y; this.description = description; } public static class NoArgsExample { @NonNull private String field; public NoArgsExample() { } } }
如果是@AllArgsConstructor 在生成的構造函數上會生成一@ConstructorProperties 的Java annotation, 當然也可以通過將suppressConstructorProperties 設置為true來禁用@ConstructorProperties 。 如果你知道@ConstructorProperties 是干什么用的,那么一定就知道@NoArgsConstructor為什么沒有這個配置參數了。
提示一點:@ConstructorProperties 只能用在JDK 6 中。
@Data,@Value
import lombok.AccessLevel; import lombok.Setter; import lombok.Data; import lombok.ToString; @Data public class DataExample { private final String name; @Setter(AccessLevel.PACKAGE) private int age; private double score; private String[] tags; @ToString(includeFieldNames=true) @Data(staticConstructor="of") public static class Exercise<T> { private final String name; private final T value; } }
使用后:
import java.util.Arrays; public class DataExample { private final String name; private int age; private double score; private String[] tags; public DataExample(String name) { this.name = name; } public String getName() { return this.name; } void setAge(int age) { this.age = age; } public int getAge() { return this.age; } public void setScore(double score) { this.score = score; } public double getScore() { return this.score; } public String[] getTags() { return this.tags; } public void setTags(String[] tags) { this.tags = tags; } @Override public String toString() { return "DataExample(" + this.getName() + ", " + this.getAge() + ", " + this.getScore() + ", " + Arrays.deepToString(this.getTags()) + ")"; } protected boolean canEqual(Object other) { return other instanceof DataExample; } @Override public boolean equals(Object o) { if (o == this) return true; if (!(o instanceof DataExample)) return false; DataExample other = (DataExample) o; if (!other.canEqual((Object)this)) return false; if (this.getName() == null ? other.getName() != null : !this.getName().equals(other.getName())) return false; if (this.getAge() != other.getAge()) return false; if (Double.compare(this.getScore(), other.getScore()) != 0) return false; if (!Arrays.deepEquals(this.getTags(), other.getTags())) return false; return true; } @Override public int hashCode() { final int PRIME = 59; int result = 1; final long temp1 = Double.doubleToLongBits(this.getScore()); result = (result*PRIME) + (this.getName() == null ? 0 : this.getName().hashCode()); result = (result*PRIME) + this.getAge(); result = (result*PRIME) + (int)(temp1 ^ (temp1 >>> 32)); result = (result*PRIME) + Arrays.deepHashCode(this.getTags()); return result; } public static 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; } @Override public String toString() { return "Exercise(name=" + this.getName() + ", value=" + this.getValue() + ")"; } protected boolean canEqual(Object other) { return other instanceof Exercise; } @Override public boolean equals(Object o) { if (o == this) return true; if (!(o instanceof Exercise)) return false; Exercise<?> other = (Exercise<?>) o; if (!other.canEqual((Object)this)) return false; if (this.getName() == null ? other.getValue() != null : !this.getName().equals(other.getName())) return false; if (this.getValue() == null ? other.getValue() != null : !this.getValue().equals(other.getValue())) return false; return true; } @Override public int hashCode() { final int PRIME = 59; int result = 1; result = (result*PRIME) + (this.getName() == null ? 0 : this.getName().hashCode()); result = (result*PRIME) + (this.getValue() == null ? 0 : this.getValue().hashCode()); return result; } } }
與@Data相對應的@Value, 兩個annotation的主要區別就是如果變量不加@NonFinal ,@Value會給所有的弄成final的。當然如果是final的話,就沒有set方法了。
import lombok.AccessLevel; import lombok.experimental.NonFinal; import lombok.experimental.Value; import lombok.experimental.Wither; import lombok.ToString; @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; } }
使用后:
import java.util.Arrays; 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 == this) return 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()) != 0) return 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 ? 0 : $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 == this) return 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 ? 0 : $name.hashCode()); final Object $value = this.getValue(); result = result * PRIME + ($value == null ? 0 : $value.hashCode()); return result; } @java.lang.Override public String toString() { return "ValueExample.Exercise(name=" + getName() + ", value=" + getValue() + ")"; } } }
@Sychronized
@Sychronized 是一個處理線程安全問題的annotation, 他的使用方法和關鍵字 synchronized比較類似,但是有一些不同點就是,關鍵字synchronized是鎖定當前對象(this指針) , 而@Synchronized則會鎖定一個private的常量。如果當前類中沒有這個常量,就會自動生成一個。
import lombok.Synchronized; 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"); } }
如果當前鎖定的方法是一個靜態的方法的話,會自動生成一個靜態常量,如果是一個普通方法的話會生成一個普通常量,類型為Object
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)
最后終於到了一個高級點的annotation的用法了,使用lazy版的getter annotation, 會提高代碼效率,同時由Lombok幫助你管理線程安全問題,大可放心。先看一段代碼:
import lombok.Getter; 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; } }
使用了getter這個annotation可以在實際使用到cached的時候生成cached,同時,Lombok會自動去管理線程安全的問題,不會存在重復賦值的問題。
Log
Lombok封裝了許多主流的Log庫,提供了一系列關於Log 的annotation。下面就是所有的annotation會代表哪些特定的類 :
@CommonsLog Creates private static final org.apache.commons.logging.Log log = org.apache.commons.logging.LogFactory.getLog(LogExample.class); @Log Creates private static final java.util.logging.Logger log = java.util.logging.Logger.getLogger(LogExample.class.getName()); @Log4j Creates private static final org.apache.log4j.Logger log = org.apache.log4j.Logger.getLogger(LogExample.class); @Log4j2 Creates private static final org.apache.logging.log4j.Logger log = org.apache.logging.log4j.LogManager.getLogger(LogExample.class); @Slf4j Creates private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(LogExample.class); @XSlf4j Creates private static final org.slf4j.ext.XLogger log = org.slf4j.ext.XLoggerFactory.getXLogger(LogExample.class);
就用其中的幾個舉個例子吧:
import lombok.extern.java.Log; import lombok.extern.slf4j.Slf4j; @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"); } }
翻譯一下,代碼如下:
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"); } }
@SneakyThrows
@SneakyThrows的用法比較簡單,其實就是對於異常的一個整理,將checked exception 看做unchecked exception, 不處理,直接扔掉。 減少了到處寫catch的不便利性。比如在線程中,catch所有異常,再比如在一些不太可能發生異常的地方,但是你又必須cache checked exception的地方使用這個annotation會顯得代碼比較規整,易讀。或許也會顯得高大上一點吧
import lombok.SneakyThrows; public class SneakyThrowsExample implements Runnable { @SneakyThrows(UnsupportedEncodingException.class) public String utf8ToString(byte[] bytes) { return new String(bytes, "UTF-8"); } @SneakyThrows public void run() { throw new Throwable(); } }
import lombok.Lombok; public class SneakyThrowsExample implements Runnable { public String utf8ToString(byte[] bytes) { try { return new String(bytes, "UTF-8"); } catch (UnsupportedEncodingException e) { throw Lombok.sneakyThrow(e); } } public void run() { try { throw new Throwable(); } catch (Throwable t) { throw Lombok.sneakyThrow(t); } } }
參考資料
http://himichaelchu.iteye.com/category/324280