为什么我认为 Lombok 应该从 Java 项目中丢弃
您好,今天的文章解决了一个看似不受欢迎的观点,我相信它会遇到一些阻力。仅仅因为某件事在技术上可行并不能自动认可其实用性或适用性。因此,我将尝试证实为什么我相信使用 lombok 可能会对您的代码产生不利影响。
揭开魔力:了解 lombok 项目
在深入研究不太流行的细节之前,让我简要解释一下 lombok 库的功能。
project lombok 充当一个库,在编译时将代码注入到类中,这看起来几乎很神奇。要理解它的操作,了解java编译过程是必不可少的。 java编译主要分为三个阶段(图1):解析与输入、注解处理、分析与生成,如下图所示:
图 1 – 抽象语法树 (ast)
解析并输入:
在这里,编译器将源文件转换为抽象语法树(ast)。仅因语法无效而引发错误,而不会因错误的类或方法使用而引发错误。注释处理:
在此阶段,自定义注释处理器会验证类或生成新资源(例如源文件)。如果生成新的源,这可能会触发新的编译周期。分析并生成:
在最后阶段,编译器从 ast 生成字节码,检查损坏的引用,验证逻辑流,执行类型擦除,并对语法糖进行脱糖。
project lombok 作为注释处理器运行,通过注入新方法、字段或表达式来修改 ast。与生成新源的典型处理器不同,lombok 会更改现有类,这一区别使其能够直接影响生成的字节码。
j2se 1.5 中引入的 annotationprocessor 无法更改现有文件。它只能创建新文件或字节码。这使得 lombok 的实现很有趣,因为它们在编译阶段使用 annotationprocessor 来修改现有的 java 类文件。这是 的概述
使用 lombok 的编译过程(
图 2)。
图2 – 编译过程和lombok
针对龙目岛的案件了解了 lombok 背后的魔力后,让我们深入探讨一下我认为它可能对您的代码库有害的原因。
增加编译时间
lombok 在编译时的操作不可避免地会延长编译过程,特别是在较大的代码库中,由于需要增加 ast 管理,这一点尤其明显。
利益错位
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()),我们确保仅公开和操作有效状态,从而保护域完整性。
通过这种深思熟虑的重组,我们证明了虽然 lombok 这样的工具可以显着减少样板代码,但它们并不能替代仔细的设计和对该领域的深入理解。它强调应该明智地使用 lombok,补充而不是掩盖强大的架构实践。这确保了我们代码的优雅不会以牺牲其正确性和清晰度为代价。
-
过度依赖@setter 和@getter
@data public class movie { private string title; private int releaseyear; } // can be replaced with: public record movie(string title, int releaseyear) {}
-
主管@nonnull
为了远离 lombok 的 @nonnull 注释并确保 java 本身的鲁棒性,java.util.objects 类中的 objects.requirenonnull() 方法非常有用。
此方法通过确保对象不为 null 来简化 null 检查,如果是,则抛出 nullpointerexception 并带有明确的消息。这种显式的异常抛出机制可以防止潜在的与 null 相关的错误在运行时出现,从而促进在开发周期中进行早期检测。这是一个示例,展示了此方法如何替换 lombok 的功能
public class nonnullexample { private student student; public nonnullexample(@nonnull student student) { this.student = student; } }等效的纯 java 方法:
import java.util.Objects; public class NonNullExample { private Student student; public NonNullExample(Student student) { this.student = Objects.requireNonNull(student, "Student cannot be null"); } }向本机 java 处理的过渡通过显式进行空检查来增强代码透明度,这有利于代码维护和理解。
构造函数的灵活性和可重用性
构造函数在类如何在软件架构中交互方面发挥着关键作用。一个设计良好的类应该有多种构造函数来适应不同的用例,从而提高可重用性和灵活性。如果您的构造函数只是复制字段分配,那么根本问题不是需要编写样板代码;而是需要编写样板代码。相反,这是形成 lombok 无法纠正的不可重用且不灵活的设计的风险。正确的构造函数设计可以让类在多种场景中集成和使用,从而增强代码库的整体健壮性和适应性。
评估样板代码:lombok 的诱惑与现代 java 功能的对比
lombok 的流行主要源于它减少样板代码的能力,特别是在传输和数据对象等特定领域的类中。 lombok 通过自动生成必要的代码(如
getters、setters、equals、hashcode 和 有效地减少了可见的混乱) tostring 方法,这种便利可能会掩盖潜在的陷阱。然而,随着 java 14 中引入的 java records 的出现,出现了一种更好的替代方案,它本身支持不可变数据载体的简洁声明。最集成
开发环境 (ide) 还能够以最少的用户输入自动生成这些样板代码,从而在 lombok 的自动化和传统 java 编码的控制之间提供平衡。
project lombok 对底层 java 版本的依赖带来了重大的兼容性风险。随着 java 的发展,抽象语法树 (ast) 结构及其解释可能会发生变化,因此需要不断更新 lombok 以确保兼容性。这会产生脆弱的依赖关系,如果 lombok 没有同时更新以支持这些更改,升级到较新的 java 版本可能会破坏您的构建。依赖非官方或私有 api 来修改类定义进一步加剧了这个问题,因为这些 api 可能在未来的 java 版本中受到限制或更改,从而威胁到 lombok 的长期生存能力。
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 项目中丢弃的详细内容,更多请关注其它相关文章!