Lombok插件如此不好?但我选择继续使用…
最近发现几个大号都在转载一篇《Lombok是让你代码处于“亚健康”状态的真正元凶》的文章,特意仔细阅读了该文,文中的观点基本上都不敢苟同,个人还是会坚持使用Lombok,除非发现一些安全和性能方面的问题。
不清楚大号转载此文是因为没仔细审阅文章内容,还是其他别的原因,但还是有必要写一篇文章表达自己的看法。至于读者是否喜欢Lombok插件,是否在实践中使用,看完本篇文章之后可根据自己的喜好和情况而定。
Lombok是什么鬼?
很多程序员都在抱怨“Java太啰嗦”或有太多的“繁文缛节”。Java语言架构师Brian Goetz也曾在一篇文章中提到:开发人员想要创建纯数据载体类(plain data carriers)通常都必须编写大量低价值、重复的、容易出错的代码。如:构造函数、getter/setter、equals()、hashCode()以及toString()等。
语言本身的问题,很难通过一两个版本能够解决,到JDK14才推出类似Lombok的Record功能。而Lombok便是一款解决上述问题的IDE插件。
在使用Lombok的过程中首先需要IDE安装对应插件,然后pom文件中引入对应依赖,依赖的scope可设为provided的。
Lombok精简代码的方式主要是通过注解来实现,其中常用的有@Data、@Getter/@Setter、@Builder、@NonNull等。如使用@Data注解,即可简单的定义一个Java Bean:
@Data public class Order { private int id; private String orderNo; private int amount; }
通过@Data,在IDE在编译时会为该类的属性提供getter/setter方法、equals方法、hashcode方法和toString方法等。
反编译之后,我们可以看到这样一个类:
public class Order { private int id; private String orderNo; private int amount; public Order() { } public int getId() { return this.id; } public String getOrderNo() { return this.orderNo; } public int getAmount() { return this.amount; } public void setId(final int id) { this.id = id; } public void setOrderNo(final String orderNo) { this.orderNo = orderNo; } public void setAmount(final int amount) { this.amount = amount; } public boolean equals(final Object o) { if (o == this) { return true; } else if (!(o instanceof Order)) { return false; } else { Order other = (Order)o; if (!other.canEqual(this)) { return false; } else if (this.getId() != other.getId()) { return false; } else { Object this$orderNo = this.getOrderNo(); Object other$orderNo = other.getOrderNo(); if (this$orderNo == null) { if (other$orderNo == null) { return this.getAmount() == other.getAmount(); } } else if (this$orderNo.equals(other$orderNo)) { return this.getAmount() == other.getAmount(); } return false; } } } protected boolean canEqual(final Object other) { return other instanceof Order; } public int hashCode() { int PRIME = true; int result = 1; int result = result * 59 + this.getId(); Object $orderNo = this.getOrderNo(); result = result * 59 + ($orderNo == null ? 43 : $orderNo.hashCode()); result = result * 59 + this.getAmount(); return result; } public String toString() { return "Order(id=" + this.getId() + ", orderNo=" + this.getOrderNo() + ", amount=" + this.getAmount() + ")"; } }
关于其他Lombok功能和注解不再多说,如果想了解可读一读相关文章。上面的实例只是展示Lombok在开发过程中带来的便利之处。下面看看那篇文章中说到的Lombok的不足之处,看你在实践的过程中是否可以忍耐,以及笔者的看法。
观点一:代码是用来阅读的
文中提到:“所有的源代码很多时候是用来阅读的,只有很少的时间是用来执行的。”
上面的说法完全赞同。但说因为使用了Lombok影响了阅读,这个就不敢苟同。
对照一下上面Order实体类的例子,感觉一下哪个更具有可读性?难道不是直接干净利索的展示属性更可读吗?
针对这一项,文中还提到Lombok隐藏了JavaBean封装的细节。如果说将通常普通的getter/setter的细节隐藏了,就算是隐藏细节了,这也太小看Java工程师了。而且,如果需要自定义的特殊的方法,可以完全自行覆盖实现。
文中还提到使用@AllArgsConstructor注解,如果参数多会生成一个巨型的构造方法。说实话,太牵强了,谁没事会生成一个巨型的构造方法呢?就是让你手写代码,你会生成吗?既然不会你干嘛在参数多的时候使用这个注解呢?适合的才是最好的,拿一个不适合使用作为反例,太牵强。
再说回到隐藏细节和可读性上,JDK14提供了Record特性,来看看Record特性的使用。
public record CustUser( String firstName, String lastName, Address address, int age ) {}
上面代码便声明了一个Record类型的类,它编译后的效果如下:
public final class CustUser extends java.lang.Record { private final java.lang.String firstName; private final java.lang.String lastName; private final com.flydean.records.Address address; private final int age; public CustUser(java.lang.String firstName, java.lang.String lastName, com.flydean.records.Address address, int age) { /* compiled code */ } public java.lang.String toString() { /* compiled code */ } public final int hashCode() { /* compiled code */ } public final boolean equals(java.lang.Object o) { /* compiled code */ } public java.lang.String firstName() { /* compiled code */ } public java.lang.String lastName() { /* compiled code */ } public com.flydean.records.Address address() { /* compiled code */ } public int age() { /* compiled code */ } }
是不是很眼熟?对的,它的效果几乎可以完美替代Lombok的功能。JDK14本身都开始往这个方向上发展了,提供了类似的语法糖效果。我们现在有什么理由不使用提供同样功能的Lombok呢?
JDK版本问题
文中提到:JDK从Java8升级到Java11时,发现Lombok不能正常工作……
Lombok可能有这样的问题,但这样的问题不是普通的问题吗?任何一个框架包括主流的SpringBoot框架,你将JDK大跨度的修改,肯定是会出现问题的。但如果是实战项目,哪家公司会搞这么高风险的事?概率太小,而且即便发生也不仅仅是Lombok一个框架会出问题。
由于Lombok是开源的,对最新版本会有一定的滞后性,如果你的项目一直处于变更最新JDK版本,那真不适用。
胁迫使用
这个的确如此,在同一个项目当中,如果一个开发者使用Lombok,那么其他的开发者都必须使用。
但就个人而言,一个项目统一使用相同的规范,相同环境,相同的IDE本来不就是应该的吗?
团队统一规范本来就是提升效率的有效手段。如果一个团队,一个人使用Eclipse,一个人使用IDEA,一个人使用文本编辑(这个牛人)来写代码。
试想一下,你去帮同事排查一个bug,调试一段代码,你是不是还得切换快捷键,反应一会儿。
其实这个可以通过行政手段来做到,你可以规定项目中不能使用Lombok,也可以规定大家都使用Lombok。就好比现在,我推荐团队使用阿里Java开发手册对应的插件一样,就为了规范代码,让大家写出的代码保持一致性。
代码耦合度增加
文章中提到一个模块使用了Lombok,其他依赖模板也必须使用,耦合性加大。解释利用同上,如果使用maven构建的话可以给lombok的依赖加上true,这样lombok的依赖不会传递给引用的项目,而此模块的jar已经生成了getter/setter方法,并不影响使用。
个人爱好的不喜欢
最后,文章提到“使用Lombok,一时觉得很爽,但它却污染了你的代码,破坏了Java代码的完整性,可读性和安全性,同时还增加的团队的技术债务,这是一种弊大于利,得不偿失的操作。”
这个可能是纯属个人喜好吧。对于完整性、可读性和安全性的影响上面也逐一说明了,更是称不上“得不偿失”。
总结一下
通过上面的分析,其实作者很多观点是立不住脚的,而且也浮于表面。
就个人而言,唯一赞同可能就是“强迫使用”,但并不是无解的。如果大家都不爱用,那就都不用,如果只有一个人用,可以通过行政性手段约束,但不能成为摒弃一项工具的理由。
这就好比,一个项目中大家都在用mybatis,突然有一个人用起了Hibernate,这比单单使用Lombok更加不可理喻吧。
再次声明,用不用纯粹喜好,不影响根本问题,不喜欢就不用。就个人而言,希望使用,也推荐使用,学习成本本身也比较低,而且如果要用就用好,发挥它的便利之处,规避它的不足。
如果你决定使用,那就继续看看下面Lombok使用中真实的坑。
Lombok的坑
第一,由于Lombok使用比较简单,而且代码也变得简单,针对初学者容易只知其然,不知其所以然。注解用的很溜,但不明白或不清楚注解到底干了什么。
其实这是学习任何一项技术都会遇到的问题,建议在学习使用新技术、新框架时对自己要求严格那么一点点。
第二,@Data定义一个类的时候,会自动帮我们生成equals()方法。如果你对equals方法和hashcode方法有特殊的要求,建议自己手动实现。
如果实体类有继承关系,那么只使用@Data注解而不使用@EqualsAndHashCode(callSuper=true)的话,此时生成的equals方法只会比较子类的属性字段,不会考虑父类的继承属性。
笔者的一点小想法
其实本篇文章本没必要写的,直白说就是爱用不用,争论的意义也并不大。知道它的缺点,知道它的优点,取其精华去其糟粕即可。
但多次看到那篇文章被转载,真的容易误导一部分读者,还没有使用Lombok,还没有了解Lombok,就因为一篇说它“不好”的文章而将其拒之门外,这不是应该的学习态度。应该了解它,熟悉它,实践它,而不仅仅是给它贴一个标签。
对于每篇文章的观点,也应该辨证的去看,当然包括本篇文章中的观点和看法。
关注公众号:程序新视界,一个让你软实力、硬技术同步提升的平台
除非注明,否则均为程序新视界原创文章,转载必须以链接形式标明本文链接