最近发现几个大号都在转载一篇《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,就因为一篇说它“不好”的文章而将其拒之门外,这不是应该的学习态度。应该了解它,熟悉它,实践它,而不仅仅是给它贴一个标签。

对于每篇文章的观点,也应该辨证的去看,当然包括本篇文章中的观点和看法。



Lombok插件如此不好?但我选择继续使用…插图

关注公众号:程序新视界,一个让你软实力、硬技术同步提升的平台

除非注明,否则均为程序新视界原创文章,转载必须以链接形式标明本文链接

本文链接:https://choupangxia.com/2020/10/20/lombok-plugin/