Lombok 在 Java Records 时代仍然相关
Lombok Is Still Relevant in the Age of Java Records

原始链接: https://swaranga.dev/posts/on-lombok/

作者认为 Lombok 尽管受到批评仍具价值。其 cited 的 bug 实为糟糕数据建模与测试缺失,而非 Lombok。Records 无法替代 Lombok 的日志、依赖注入构造器、构建器及其他样板减少。使用数据显示日志与 RequiredArgsConstructor 占主导。编译时生成比 Jackson、Hibernate 等框架的不透明运行时行为更透明。升级担忧被夸大;Lombok 通常快速修补,若真出问题,delombok 可生成源码解除锁定。务实胜过纯粹。

Hacker News 用户争论 Lombok 在 Java records 之外是否仍然有用。批评者称其为“黑魔法”,会隐藏行为、鼓励糟糕设计,并随着 IDE、JDK21 构造器要求和 Java records 覆盖常见场景而越来越不必要。一些人指出 Lombok 的构建器和日志注解仍有帮助,但认为许多功能(@Setter、@Value、@Cleanup)会促进可变性或被语言特性取代。其他人认为 AI 编码代理使避免样板代码的库显得过时,但反方强调抽象和可维护性仍然重要。少数评论者广泛批评 Java 本身面向企业且积累了大量冗余。总体而言,情绪褒贬不一,但更倾向于 Lombok 对现代 Java 增加不多,剩余价值主要在于构建器和便利注解。
相关文章

原文

Every six months, like clockwork, a new JDK drops—and right on cue, the Java community’s ideological purity tests begin.

The usual suspects crawl out to complain about Project Lombok. They argue that it does “weird hacks” to the javac compiler, that it slows down upgrades, and that we don’t need it anymore because we have Records.

I am a little tired of this constant principled stance against its usage. I am an application developer, not a compiler engineer. I’ve been using Lombok for roughly 15 years now, and I can clearly say its benefits massively outweigh its cons. I’m glad I don’t work with people who hold that dogmatic opinion, because at the end of the day, we are paid to deliver working software—not to coddle the compiler.

I’m still betting on Lombok, and for me, the arguments against it usually fall flat in the real world.

Lombok introduces “weird bugs”

In over a decade and a half of using Lombok, I have only seen exactly one issue where Lombok was called out in a post-mortem as the root cause—and even then, the blame was unfair IMO.

We had a class annotated with @Data containing a primitive boolean field. When deserializing a message from SQS, the incoming payload was missing this field. Because it was defined as a primitive, Jackson assigned it the default value of false, which accidentally triggered a different code path.

The post-mortem labeled it a “Lombok issue.”

I disagree. Lombok simply generated the setter. The true root causes were:

  1. Bad data modeling: The field logically required three valid states (true, false, and null), so it should have been a boxed Boolean type from the start.
  2. Lack of test coverage: This edge case should have been caught in testing immediately, or flagged during the PR review.

Blaming Lombok for a developer coding error and a gap in test coverage is just lazy.

The Myth of “We Have Records Now”

Whenever you defend Lombok, someone inevitably chimes in with, “Just use Records!”

Records are fantastic for immutable data carriers, but they are not a complete replacement for Lombok. To prove this, I did a quick check on my current project to see which Lombok features we actually rely on.

Here is the raw data:

find . -name "*.java" | xargs cat | grep "import lombok" | sort | uniq -c | sort -n -r

    144 import lombok.extern.slf4j.Slf4j;
    131 import lombok.NonNull;
    120 import lombok.RequiredArgsConstructor;
     68 import lombok.Builder;
     30 import lombok.Data;
     24 import lombok.Getter;
     22 import lombok.NoArgsConstructor;
     22 import lombok.AllArgsConstructor;
     12 import lombok.experimental.Accessors;
      8 import lombok.extern.jackson.Jacksonized;
      6 import lombok.EqualsAndHashCode;
      4 import lombok.AccessLevel;
      2 import lombok.Singular;
      2 import lombok.experimental.UtilityClass;
      1 import lombok.ToString;
      1 import lombok.extern.log4j.Log4j2;

What dominates the top of that list is not @Data. It’s logging and code to support dependency injection.

I cannot imagine coding without @RequiredArgsConstructor. In Spring, you just declare your injected services as final fields, add that one annotation, and your constructor boilerplate vanishes. Records do absolutely nothing to solve this. Add @Slf4j and @Builder to the mix, and you are saving hundreds of lines of tedious, error-prone boilerplate per module that you’d otherwise have to write, review, and maintain.

And as for IDEs generating getters and setters? You still have to read that cluttered code. Plus, adding a new field means remembering to manually regenerate the equals and hashCode methods; a step that is easy to forget.

The Lombok “Dark Magic”

Purists argue that Lombok’s AST manipulation is “magic” and that modifying the compiler’s behavior is an architectural sin. But these same developers will happily build entire enterprise systems on top of frameworks that do far worse things at runtime.

Take Jackson, for example. The Java purist wants everything to be explicit and strictly abide by JVM rules. Yet, when Jackson deserializes a JSON payload into an object that lacks a default constructor, it often uses reflection (or libraries like Objenesis) to allocate memory for that object without ever actually calling a constructor. You are upset that Lombok generates a predictable setName() method at compile time, but you are perfectly fine with a framework completely bypassing the JVM’s object-creation lifecycle at runtime?

Or look at Hibernate and JPA. You define a standard List<Order> in your entity and write your business logic against it. But at runtime, Hibernate silently intercepts your object and replaces your standard Java ArrayList with its own PersistentBag proxy. If you try to iterate over that list after the database session closes, your application explodes with the dreaded LazyInitializationException.

That is more opaque, runtime sorcery where standard data structures are hijacked in an invisible manner.

Compared to that, Lombok is incredibly boring. The generated code exists completely at compile time. You can view the .class files. You can see exactly what it generated, and if a getter returns a value, you can step right through it. If we are willing to tolerate the massive, opaque runtime machinery of Jackson and Hibernate because of the velocity they provide, we should absolutely apply that exact same pragmatism to Lombok. AOP is another such tool with lots of gotchas - I remember once debugging a Try-Catch block in an AWS SWF (Simple Workflow) FLow Framework implementation where the Try and Catch blocks were literally executing on different machines.

A future JDK release will completely break Lombok’s compiler hacks

First, the upgrade panic is overblown. Unless you are raring to upgrade on day zero, Lombok is usually patched and ready within days of a major release. (I’m already running JDK 26 with Lombok).

But let’s say the absolute worst-case scenario happens, and Lombok is permanently banned from future JDKs. If that day comes, my migration strategy is incredibly simple:

  • Run delombok on my source code.
  • Commit the generated boilerplate and remove the dependency.
  • Upgrade the JDK in a separate PR.

There is zero runtime lock-in. Until that hypothetical doomsday arrives, I will gladly save my time and skip writing code that I simply don’t have to write. Purity is great in theory, but pragmatism ships features.

联系我们 contact @ memedata.com