什么是Lombok
Lombok能够以简单的注解形式来简化java代码,提高开发人员的开发效率。例如开发中创建javabean,需要添加相应的getter/setter、构造器、equals等方法,而且当属性过多时会出现大量的getter/setter方法。Lombok能通过注解的方式,在编译时自动为属性生成getter/setter、构造器、equals等方法。在源码中没有这些方法,但在编译生成字节文件中有这些方法。这样就省去手动重建这些代码的麻烦,并使代码看起来更简洁。
节省代码量的事怎么能错过,我们下面来实际操作下。
具体实现
插件安装
使用Lombok需要安装插件,这样在编译时才不会报错。
Maven依赖
<!-- lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<scope>provided</scope>
</dependency>
@Data
@Data注解在类上,会为类的所有属性自动生成getter/setter、toString、equals、hashCode方法。如果为final属性,则不会为该属性生成setter方法。其实@Data相当于@Getter、@Setter、@RequiredArgsConstructor、@ToString和@EqualsAndHashCode的集合。
官方实例如下:
import lombok.AccessLevel;
import lombok.Data;
import lombok.Setter;
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;
}
}
如果不使用Lombok,则实现如下:
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 ? 43 : 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 ? 43 : this.getName().hashCode());
result = (result * PRIME) + (this.getValue() == null ? 43 : this.getValue().hashCode());
return result;
}
}
}
@Getter / @Setter
@Getter/@Setter可以注解在类和属性上。注解在类上,会为类的所有属性生成getter方法,会为类的所有非final属性生成setter方法。注解在属性上,可以为当前属性生成getter/setter方法。
官方实例如下:
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);
}
}
如果不使用Lombok,则实现如下:
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;
}
}
@EqualsAndHashCode
@EqualsAndHashCode注解在类上,默认会使用非静态(non-static)和非瞬态(non-transient)属性来生成equals、canEqual和hashCode方法,也能通过exclude注解来排除一些属性。
官方实例如下:
import lombok.EqualsAndHashCode;
@EqualsAndHashCode
public class EqualsAndHashCodeExample {
private transient int transientVar = 10;
private String name;
private double score;
@EqualsAndHashCode.Exclude
private Shape shape = new Square(5, 10);
private String[] tags;
@EqualsAndHashCode.Exclude
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;
}
}
}
如果不使用Lombok,则实现如下:
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 ? 43 : 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;
}
}
}
@ToString
@ToString注解在类上,会为类的生成toString方法。默认会输出类名、所有属性(按照属性定义顺序),用逗号分隔。通过将includeFieldNames参数设为true,就能同时输出属性名。
官方实例如下:
import lombok.ToString;
@ToString
public class ToStringExample {
private static final int STATIC_VAR = 10;
private String name;
private Shape shape = new Square(5, 10);
private String[] tags;
@ToString.Exclude
private int id;
public String getName() {
return this.name;
}
@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,则实现如下:
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.name;
}
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) + ")";
}
}
@NoArgsConstructor / @RequiredArgsConstructor / @AllArgsConstructor
注解在类上,会为类的生成无参构造器、部分参数构造器、全参构造器。Lombok没法实现多种参数构造器的重载。
官方实例如下:
import lombok.*;
@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;
}
}
如果不使用Lombok,则实现如下:
public class ConstructorExample<T> {
private int x, y;
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 {
private String field;
public NoArgsExample() {
}
}
}
@NonNull
@NonNull注解在属性、方法或构造器上,Lombok会生成一个空检查语句。
官方实例如下:
import lombok.NonNull;
public class NonNullExample extends Something {
private String name;
public NonNullExample(@NonNull Person person) {
super("Hello");
this.name = person.getName();
}
}
如果不使用Lombok,则实现如下:
public class NonNullExample extends Something {
private String name;
public NonNullExample(Person person) {
super("Hello");
if (person == null) {
throw new NullPointerException("person is marked @NonNull but is null");
}
this.name = person.getName();
}
}
@Cleanup
@Cleanup注解在本地变量中,可以确保在代码执行路径退出当前范围之前自动清理给定资源。 官方实例如下:
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);
}
}
}
如果不使用Lombok,则实现如下:
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();
}
}
}
}
@Log
@Log注解在类上,Lombok会为当前类生成日志记录器,名为log。
// @Log
private static final java.util.logging.Logger log = java.util.logging.Logger.getLogger(LogExample.class.getName());
// @Log4j
private static final org.apache.log4j.Logger log = org.apache.log4j.Logger.getLogger(LogExample.class);
// @Log4j2
private static final org.apache.logging.log4j.Logger log = org.apache.logging.log4j.LogManager.getLogger(LogExample.class);
// @Slf4j
private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(LogExample.class);
官方实例如下:
import lombok.extern.java.Log;
import lombok.extern.slf4j.Slf4j;
@Log
public class LogExample {
public static void main(String... args) {
log.severe("Something's wrong here");
}
}
@Slf4j
public class LogExampleOther {
public static void main(String... args) {
log.error("Something else is wrong here");
}
}
总结
Lombok的优缺点很明显,如下:
优点:
- 能够通过注解的方式生成构造器、getter/setter、equals、hashcode、toString等方法,提高了一定的开发效率
- 让代码变得简洁,不用过多的去关注相应的方法
- 属性做修改时,也简化了为这些属性重新生成新的getter/setter方法等
缺点:
- 需要安装插件
- 不支持多种参数构造器的重载
但是,当你尝试使用过它之后,你很难再不用它。