先看再点赞,给自己一点思考的时间,微信搜索【果汁简历】公众号,获取更多内容。
这两天关于 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.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.10</version>
<scope>provided</scope>
</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获取。
再说弊端
- IDE 依赖
上文中引入的 pom.xml 的依赖只是在 maven 编译的时候有作用,如果使用 IDEA,本身也需要安装插件,这样对于新拉取代码的同学无疑是一项额外的配置,当然私以为就像是安装 JDK 一样,这个操作并不繁琐。
- JDK版本兼容
当我想要将现有项目的JDK从Java 8升级到Java 11时,Lombok 会存在不兼容的问题,不过反过来讲,这并不是一个致命问题,线上 run 的很好的项目,会那么轻易的就切换 JDK 版本么?如果是针对新的项目,自然有更多的选择。JDK 并没有万不得已要升级的原因,如果真的有也可以用 Delombok 完成。
- 调试困难
很多读者反馈,因为没有源码 debug 困难,不过对于贫血模型来说,debug setter
,getter
,toString
和 equals
方法的机会也太少了吧,倘若你选择的是充血模型,那么自然也不会选择 Lombok
了。
孰轻孰重
通看利弊以后我们发现,好的地方很明显,就是效率,同时也伴随一些问题,但是倒不是不能接受,依赖、兼容、调试、可读性其实相对于效率来说都略显单薄,所以小编倒是推荐使用 Lombok
,从效率角度出发,把它仅仅当做一个提效的工具,并不是争相谈论的话题。当然看下下面的图, Lombok
既然能在 Top Downloads 位居第 6 名也不是刷出来的啦。
所以你的意思是什么?欢迎留言区说出你的想法,没准可以擦出火花哦。