让人又爱又恨的 Lombok,到底该不该用
共 9254字,需浏览 19分钟
·
2020-08-03 00:22
点击上方 果汁简历 ,选择“置顶公众号”
优质文章,第一时间送达
这两天关于 Lombok 的文章层出不穷,众说纷纭,那么到底是该不该用呢?
先说利好
先说一下它可以做什么,最简单的用法,帮助我们生成setter
,getter
,toString
和 equals
方法。如果我们不是用 Lombok
需要按照下面的方案写什么内容,我们也能看得出来这都是样板代码,没啥实质性
import java.util.Objects;
public class User {
private String id;
private String name;
private String desc;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getDesc() {
return desc;
}
public void setDesc(String desc) {
this.desc = desc;
}
@Override
public String toString() {
return "User{" +
"id='" + id + '\'' +
", name='" + name + '\'' +
", desc='" + desc + '\'' +
'}';
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
User user = (User) o;
return Objects.equals(id, user.id) &&
Objects.equals(name, user.name) &&
Objects.equals(desc, user.desc);
}
@Override
public int hashCode() {
return Objects.hash(id, name, desc);
}
}
那么我们如果使用 Lombok
呢?引入依赖
<dependency>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
<version>1.18.10version>
<scope>providedscope>
dependency>
代码
import lombok.Data;
@Data
public class User {
private String id;
private String name;
private String desc;
}
就这样就写好了,是不是非常简单?Lombok
还提供了很多其他扩展的注解能力如下
@NonNull:用于做空指针异常检测; @Cleanup:自动资源关闭,这个注解可以直接在流上面使用,@Cleanup InputStream inputStream = new FileInputStream(); @Getter/@Setter:自动生成get/set方法; @ToString:生成toString方法,方便打印调试; @EqualsAndHashCode:生成equals和hashCode方法,注意这两个应该同时去实现; @NoArgsConstructor, @RequiredArgsConstructor and @AllArgsConstructor:构造方法; @Data:相当于 @ToString + @EqualsAndHashCode + @Getter + @Setter + @RequiredArgsConstructor。 @Builder:生成builder方法; @SneakyThrows:自动抛受检异常,而无需显式在方法上使用 throws 语句; @Synchronized:用在方法上,将方法声明为同步的,并自动加锁。 @Log:日志相关,如果我们打了@XSlf4j,任何 class 都可以直接使用 log变量获取到日志对象实例。
@CommonsLog:org.apache.commons.logging.LogFactory.getLog(LogExample.class);
@Flogger:com.google.common.flogger.FluentLogger.forEnclosingClass();
@JBossLog:org.jboss.logging.Logger.getLogger(LogExample.class);
@Log:java.util.logging.Logger.getLogger(LogExample.class.getName());
@Log4j:org.apache.log4j.Logger.getLogger(LogExample.class);
@Log4j2:org.apache.logging.log4j.LogManager.getLogger(LogExample.class);
@Slf4j:org.slf4j.LoggerFactory.getLogger(LogExample.class);
@XSlf4j:org.slf4j.ext.XLoggerFactory.getXLogger(LogExample.class);
@CustomLog:com.foo.your.LoggerFactory.createYourLogger(LogExample.class);
下面是一些比较难理解的注解实例,直接可以通过注释查看用法,后面附上了 IDEA 反编译的 class 便一目了然。
import lombok.*;
import java.io.FileInputStream;
import java.io.InputStream;
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class User {
private String id;
private String name;
private String desc;
public static void main(String[] args) {
// @NoArgsConstructor 生成
new User();
// @AllArgsConstructor 生成
new User("juice-resume", "果汁简历", "专注于 Java 方向技术分享");
// @Builder 生成
User user = User.builder().id("juice-resume").name("果汁简历").desc("专注于 Java 方向技术分享").build();
}
// @SneakyThrows 自动生成 throws FileNotFoundException
@SneakyThrows
private void read() {
// @Cleanup 相当于 inputStream.close();
@Cleanup
InputStream inputStream = new FileInputStream("");
}
/**
* @Synchronized private final Object $lock = new Object[0];
* public void write() {
* synchronized(this.$lock) {
* System.out.println("write");
* }
* }
*/
@Synchronized
public void write() {
System.out.println("write");
}
}
IDEA 反编译的 class 文件
import java.io.FileInputStream;
import java.io.InputStream;
import java.util.Collections;
public class User {
private final Object $lock = new Object[0];
private String id;
private String name;
private String desc;
public static void main(String[] args) {
new User();
new User("juice-resume", "果汁简历", "专注于 Java 方向技术分享");
User user = builder().id("juice-resume").name("果汁简历").desc("专注于 Java 方向技术分享").build();
}
private void read() {
try {
InputStream inputStream = new FileInputStream("");
if (Collections.singletonList(inputStream).get(0) != null) {
inputStream.close();
}
} catch (Throwable var2) {
throw var2;
}
}
public void write() {
synchronized(this.$lock) {
System.out.println("write");
}
}
public static User.UserBuilder builder() {
return new User.UserBuilder();
}
public String getId() {
return this.id;
}
public String getName() {
return this.name;
}
public String getDesc() {
return this.desc;
}
public void setId(final String id) {
this.id = id;
}
public void setName(final String name) {
this.name = name;
}
public void setDesc(final String desc) {
this.desc = desc;
}
public boolean equals(final Object o) {
if (o == this) {
return true;
} else if (!(o instanceof User)) {
return false;
} else {
User other = (User)o;
if (!other.canEqual(this)) {
return false;
} else {
label47: {
Object this$id = this.getId();
Object other$id = other.getId();
if (this$id == null) {
if (other$id == null) {
break label47;
}
} else if (this$id.equals(other$id)) {
break label47;
}
return false;
}
Object this$name = this.getName();
Object other$name = other.getName();
if (this$name == null) {
if (other$name != null) {
return false;
}
} else if (!this$name.equals(other$name)) {
return false;
}
Object this$desc = this.getDesc();
Object other$desc = other.getDesc();
if (this$desc == null) {
if (other$desc != null) {
return false;
}
} else if (!this$desc.equals(other$desc)) {
return false;
}
return true;
}
}
}
protected boolean canEqual(final Object other) {
return other instanceof User;
}
public int hashCode() {
int PRIME = true;
int result = 1;
Object $id = this.getId();
int result = result * 59 + ($id == null ? 43 : $id.hashCode());
Object $name = this.getName();
result = result * 59 + ($name == null ? 43 : $name.hashCode());
Object $desc = this.getDesc();
result = result * 59 + ($desc == null ? 43 : $desc.hashCode());
return result;
}
public String toString() {
return "User(id=" + this.getId() + ", name=" + this.getName() + ", desc=" + this.getDesc() + ")";
}
public User() {
}
public User(final String id, final String name, final String desc) {
this.id = id;
this.name = name;
this.desc = desc;
}
public static class UserBuilder {
private String id;
private String name;
private String desc;
UserBuilder() {
}
public User.UserBuilder id(final String id) {
this.id = id;
return this;
}
public User.UserBuilder name(final String name) {
this.name = name;
return this;
}
public User.UserBuilder desc(final String desc) {
this.desc = desc;
return this;
}
public User build() {
return new User(this.id, this.name, this.desc);
}
public String toString() {
return "User.UserBuilder(id=" + this.id + ", name=" + this.name + ", desc=" + this.desc + ")";
}
}
}
好了,说了这么多该总结了,Lombok确实能给我们带来极大的便利,减少大量重复、无业务逻辑的代码,提高开发效率,那么它由什么缺点呢?更多IDEA相关技术可以关注微信订阅号果汁简历回复IDEA获取。
再说弊端
1. IDE 依赖
上文中引入的 pom.xml 的依赖只是在 maven 编译的时候有作用,如果使用 IDEA,本身也需要安装插件,这样对于新拉取代码的同学无疑是一项额外的配置,当然私以为就像是安装 JDK 一样,这个操作并不繁琐。
2. JDK版本兼容
当我想要将现有项目的JDK从Java 8升级到Java 11时,Lombok 会存在不兼容的问题,不过反过来讲,这并不是一个致命问题,线上 run 的很好的项目,会那么轻易的就切换 JDK 版本么?如果是针对新的项目,自然有更多的选择。JDK 并没有万不得已要升级的原因,如果真的有也可以用 Delombok 完成。
3. 调试困难
很多读者反馈,因为没有源码 debug 困难,不过对于贫血模型来说,debug setter
,getter
,toString
和 equals
方法的机会也太少了吧,倘若你选择的是充血模型,那么自然也不会选择 Lombok
了。
孰轻孰重
通看利弊以后我们发现,好的地方很明显,就是效率,同时也伴随一些问题,但是倒不是不能接受,依赖、兼容、调试、可读性其实相对于效率来说都略显单薄,所以小编倒是推荐使用 Lombok
,从效率角度出发,把它仅仅当做一个提效的工具,并不是争相谈论的话题。当然看下下面的图, Lombok
既然能在 Top Downloads 位居第 6 名也不是刷出来的啦。
2. 一个空格引发的“惨案“
3. 大型网站架构演化发展历程
扫码二维码关注我
·end·
—如果本文有帮助,请分享到朋友圈吧—
我们一起愉快的玩耍!