首页 > Java > java教程 > 正文

为什么我认为 Lombok 应该从 Java 项目中丢弃

PHPz
发布: 2024-09-03 14:01:08
原创
654 人浏览过

您好,今天的文章解决了一个看似不受欢迎的观点,我相信它会遇到一些阻力。仅仅因为某件事在技术上可行并不能自动认可其实用性或适用性。因此,我将尝试证实为什么我相信使用 Lombok 可能会对您的代码产生不利影响。

揭开魔力:了解 Lombok 项目

在深入研究不太流行的细节之前,让我简要解释一下 Lombok 库的功能。

Project Lombok 充当一个库,在编译时将代码注入到类中,这看起来几乎很神奇。要理解它的操作,了解Java编译过程是必不可少的。 Java编译主要分为三个阶段(图1):解析与输入、注解处理、分析与生成,如下图所示:

Why I Believe Lombok Should Be Discarded from Java Projects

图 1 – 抽象语法树 (AST)

  1. 解析并输入:
    在这里,编译器将源文件转换为抽象语法树(AST)。仅因语法无效而引发错误,而不会因错误的类或方法使用而引发错误。

  2. 注释处理:
    在此阶段,自定义注释处理器会验证类或生成新资源(例如源文件)。如果生成新的源,这可能会触发新的编译周期。

  3. 分析并生成:
    在最后阶段,编译器从 AST 生成字节码,检查损坏的引用,验证逻辑流,执行类型擦除,并对语法糖进行脱糖。

Project Lombok 作为注释处理器运行,通过注入新方法、字段或表达式来修改 AST。与生成新源的典型处理器不同,Lombok 会更改现有类,这一区别使其能够直接影响生成的字节码。

J2SE 1.5 中引入的 AnnotationProcessor 无法更改现有文件。它只能创建新文件或字节码。这使得 lombok 的实现很有趣,因为它们在编译阶段使用 AnnotationProcessor 来修改现有的 java 类文件。这是 的概述 使用 Lombok 的编译过程(
图 2)。

Why I Believe Lombok Should Be Discarded from Java Projects

图2 – 编译过程和Lombok

针对龙目岛的案件

了解了 Lombok 背后的魔力后,让我们深入探讨一下我认为它可能对您的代码库有害的原因。

增加编译时间

Lombok 在编译时的操作不可避免地会延长编译过程,特别是在较大的代码库中,由于需要增加 AST 管理,这一点尤其明显。

利益错位

Lombok 提供了各种注释,可能会给人一种解决基本编程挑战的错觉。我将讨论我在代码库中经常遇到的一些注释。

    @Builder注解
Lombok 中的 @Builder 注释通过构建器模式简化了对象创建,增加了一层最初吸引人的便利。考虑这个例子:


@Data
@Builder
public class Course {
    public enum Type {
        ONLINE,
        ONSITE;

        @JsonValue
        @Override
        public String toString() {
            return super.toString().toLowerCase();
        }
    }

    private long id;
    private Type type;
}
登录后复制
及其用法:


public class CourseCreator {

    public static Course createCourse(Enrollment enrollment, Registration registration) {
        Course.Type courseType = enrollment.getVenue().equals(registration.getVenue()) ? Course.Type.ONSITE : Course.Type.ONLINE;

        return Course.builder()
            .id(enrollment.getId())
            .type(courseType)
            .build();
    }

     public static void main(String[] args) {

        Registration registration = new Registration(); 
        Enrollment enrollment = new Enrollment();
        Course course = createCourse(enrollment, registration);
        System.out.println(course);
    }
}
登录后复制
虽然构建器模式得到了有效的实现,但也引发了有关所创建对象的完整性和有效性的关键问题。

如果我们在构建器中省略 .type(),我们将实例化什么类型的课程?

这行代码可以编译,但它让我们产生疑问:我们实际上创建了什么类型的课程?这是有效的课程实例吗?


Course.builder().id(1L).build();
登录后复制
这些担忧表明,开发人员可能会受到注释便利性的影响,可能会忽视维护业务逻辑完整性所需的彻底的域建模。不要让 Lombok 决定我们的设计,更重要的是确保与业务需求保持一致的深思熟虑的方法。

考虑调整实施,以确保任何课程创建都是明确的并受到业务环境的约束:


@Data
public class Course {
    private enum Type {
        ONLINE,
        ONSITE;

        @JsonValue
        @Override
        public String toString() {
            return super.toString().toLowerCase();
        }
    }

    public static Course online(long id) {
        return new Course(id, Type.ONLINE);
    }

    public static Course onsite(long id) {
        return new Course(id, Type.ONSITE);
    }

    private long id;
    private Type type;

    public boolean isOnline() {
        return Type.ONLINE.equals(this.type);
    }

    public boolean isOnsite() {
        return Type.ONSITE.equals(this.type);
    }
}
登录后复制
通过重新设计类:


public class CourseManagement {
    public static Course createAppropriateCourse(Enrollment enrollment, Registration registration) {
        return enrollment.getVenue().equals(registration.getVenue()) ? 
            Course.onsite(enrollment.getId()) : 
            Course.online(enrollment.getId());
    }

    public static void main(String[] args) {
        Registration registration = new Registration();
        Enrollment enrollment = new Enrollment();
        Course createdCourse = createAppropriateCourse(enrollment, registration);

        System.out.println(createdCourse);
    }
}
登录后复制
修改后的设计确保了 Course 对象的创建是明确且万无一失的,反映了领域固有的受限选择并消除了歧义。

此外,通过将 Type 枚举设为私有并提供清晰、显式的方法(如 isOnline() 和 isOnsite()),我们确保仅公开和操作有效状态,从而保护域完整性。

Through this thoughtful restructuring, we demonstrate that while tools like Lombok can significantly reduce boilerplate, they are not substitutes for careful design and a deep understanding of the domain. It underscores that Lombok should be employed judiciously, complementing rather than overshadowing robust architectural practices. This ensures that the elegance of our code does not come at the expense of its correctness and clarity.

  • Overreliance on @Setter and @Getter

The argument that getters and setters reduce boilerplate falls short when Java offers alternatives like the Record classes from Java 14.

@Data
public class Movie {
    private String title;
    private int releaseYear;
}

// Can be replaced with:
public record Movie(String title, int releaseYear) {}
登录后复制
  • Superintendent @NonNull

Having null in your code - aside from inputs is generally considered problematic and is often indicative of deeper design issues. The prevalent advice is to avoid returning null whenever possible. Instead, opt for alternatives such as returning non-null collections, utilizing null objects, or throwing exceptions to signify unusual or exceptional conditions. This strategic avoidance means null checks become redundant in most parts of your code.

To distance from Lombok's @NonNull annotation and ensure robustness in Java natively, the Objects.requireNonNull() method from the java.util.Objects class is incredibly useful.
This method streamlines null checking by ensuring that an object is not null, and it throws a NullPointerException with a clear message if it is. This explicit exception-throwing mechanism prevents latent null-related bugs from surfacing in runtime, promoting earlier detection during the development cycle. Here’s an example showing how this method can replace Lombok's functionality

Using Lombok's@NonNull:

public class NonNullExample {
    private Student student;

    public NonNullExample(@NonNull Student student) {
        this.student = student;
    }
}
登录后复制

Equivalent pure Java approach:

import java.util.Objects;

public class NonNullExample {
    private Student student;

    public NonNullExample(Student student) {
        this.student = Objects.requireNonNull(student, "Student cannot be null");
    }
}
登录后复制

This transition to native Java handling enhances code transparency by making the null-check explicit, which is advantageous for code maintenance and understanding.

Constructor Flexibility and Reusability

Constructors play a critical role in how classes interact within your software architecture. A well-designed class should have a variety of constructors that accommodate different use cases, promoting reusability and flexibility. If your constructors merely replicate field assignments, the underlying issue isn't the need to write boilerplate code; rather, it's the risk of fostering a non-reusable and inflexible design that Lombok cannot rectify. Proper constructor design allows a class to be integrated and utilized in a multitude of scenarios, enhancing the overall robustness and adaptability of your codebase.

Evaluating Boilerplate Code: The Lure of Lombok versus Modern Java Features

Lombok's popularity predominantly stems from its ability to reduce boilerplate code, particularly in domain-specific classes like transfer and data objects. While Lombok effectively diminishes the visible clutter by auto-generating necessary code like getters, setters, equals, hashCode, and toString methods, this convenience might obscure potential pitfalls. However, with the advent of Java Records introduced in Java 14, there is a preferable alternative that natively supports the concise declaration of immutable data carriers. Most integrated
development environments (IDEs) are also equipped to automatically generate these boilerplate codes with minimal user input, offering a balance between Lombok’s automation and the control of traditional Java coding.

Compatibility Concerns

Project Lombok's dependency on the underlying Java version poses a significant compatibility risk. As Java evolves, the Abstract Syntax Tree (AST) structure and its interpretation could change, necessitating continuous updates to Lombok to ensure compatibility. This creates a fragile dependency where upgrading to a newer Java version could potentially break your build if Lombok is not simultaneously updated to support these changes. The reliance on unofficial or private APIs to modify class definitions further exacerbates this issue because these APIs could be restricted or altered in future Java releases, threatening Lombok’s long-term viability.

Java 标准

使用仅使用标准 Java 编译器选项的工具构建项目时,使用 Lombok 可能会导致复杂化。例如,如果您的代码使用 Lombok 生成的 getter 和 setter,则直接使用 javac 进行编译而不进行 Lombok 预处理可能会导致错误,指示缺少方法。虽然有些人可能认为 Lombok 注入代码的能力是一个聪明的“技巧”,但批判性地评估相关风险和替代方案至关重要。问题的核心在于Java的注解处理规范并不正式支持在编译期间修改现有的类。依赖这些非官方技术使得 Lombok 容易受到未来 Java 更新的影响,这些更新可能会破坏或禁用其功能。

最终,这些考虑因素强调了不仅要评估 Lombok 等工具的直接好处的重要性,还要评估它们对可维护性、兼容性以及与 Java 标准的一致性的长期影响。随着 Java 的不断发展,对稳定、标准化功能的依赖对于确保软件项目的可持续性和可靠性变得越来越重要。

结论

Lombok 似乎是 Java 开发的便捷捷径,但它将 Java 代码转换为特定于域的版本,我喜欢称之为 “Lombok Java”。必须认识到,过度依赖 Lombok 可能会掩盖 Java 本质,可能会导致代码不够健壮,并且在没有 Lombok 的情况下更难以管理。

如果过度依赖 Lombok 是管理代码库的解决方案,那么可能是时候重新评估底层架构和实践了。 Java 的真正优势在于其清晰性和结构,而不是外部库提供的快捷方式。

如果有机会,我会选择从我的项目中放弃 Lombok。

以上是为什么我认为 Lombok 应该从 Java 项目中丢弃的详细内容。更多信息请关注PHP中文网其他相关文章!

来源:dev.to
本站声明
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn
热门教程
更多>
最新下载
更多>
网站特效
网站源码
网站素材
前端模板
关于我们 免责声明 Sitemap
PHP中文网:公益在线PHP培训,帮助PHP学习者快速成长!