Skip to main content

Exploring Lombok’s val: Advantages and Disadvantages

·6 mins
Michael Hönnig
Author
Michael Hönnig
Until autumn 2027 only available for consulting engagements with a small number of hours – remote, in German and English.

If you’re already using Lombok in a project, you’re likely leveraging its concise and boilerplate-reducing features. Among these features is Lombok’s val, a powerful tool that introduces local variable type inference into Java. Even though Java’s own var keyword (introduced in Java 10) provides similar functionality, there may still be situations in which Lombok’s val feels like an appealing option. However, before adopting val broadly, it is essential to evaluate what it is, how it works, and the risks and advantages associated with it—especially given the ongoing evolution of Java.

What is Lombok’s val?
#

Lombok’s val is a real generic type provided by the Lombok library. You can declare local variables using val as their type, and Lombok’s annotation processor recognizes this special type during compilation. The processor then infers the actual type from the initializer expression and replaces the val type with the concrete type, while also making the variable final.

For instance:

import lombok.val;

// ...

val myList = new ArrayList<String>();

Note that val must be imported ( import lombok.val;) for the code to compile; using it as lombok.val whould possible, but quite ugly.

Here, you’re declaring a variable of type val, which is valid Java syntax. Lombok’s processor will infer that myList is of type ArrayList<String>, make the variable final, and transform the code to:

final ArrayList<String> myList = new ArrayList<>();

How Does Lombok’s val Work Under the Hood?
#

Lombok uses Java’s annotation processing mechanism to detect the use of the val type in variable declarations. Specifically, Lombok:

  1. Scans your code for val type declarations.

  2. Analyzes the initializer expression to determine its actual type.

  3. Rewrites the code in the Abstract Syntax Tree (AST) before the compiler moves to the type checking phase, replacing val with the inferred concrete type and adding the final modifier.

For example, when you write:

val x = 42;

Lombok transforms this into:

final int x = 42;

This transformation occurs between Java’s parsing phase (which validates the syntax) and its type checking phase (which ensures type compatibility). By the time the type checker analyzes the code, Lombok has already replaced the val type with the appropriate final type. The fact that val is a real, importable type means this transformation is completely compatible with Java’s standard compilation process.

Advantages of Lombok’s val
#

Given that Lombok is already part of your project, the additional benefits of using val include:

  1. Cleaner and More Readable Code:

    • Eliminates repetitive boilerplate such as explicitly specifying types that are already clear from the initializer.

    • Improves focus on the logic rather than the types.

    val result = someMethod(); // Cleaner than explicitly declaring the return type
  2. Compile-Time Safety:

    • Unlike dynamic languages, Lombok’s val ensures static type checking. If the initializer’s type is incompatible, the compiler will produce an error.
  3. Immutability Enforcement:

    • All val variables are automatically final, reducing accidental reassignment and promoting immutability practices.
  4. Consistency Across All Java Versions:

    • You can use val for type inference even if your project is running on a Java version prior to Java 10, where var wasn’t available.
  5. Part of the Lombok Ecosystem:

    • If your project is already relying on Lombok for features like @Data, @Builder, or @Getter, val integrates seamlessly with no additional dependencies.

Disadvantages of Lombok’s val
#

However, there are some trade-offs to consider:

  1. Redundancy with var (Java 10 or Later):

    • Since Java natively supports type inference with the var keyword, using Lombok’s val in modern projects may seem redundant and unnecessary.
    // Native Java solution (Java 10+)
    var myList = new ArrayList<String>();
  2. Less Flexible Than var in Non-Final Scenarios:

    • Unlike var, Lombok’s val enforces immutability by making variables final. For mutable variables, you need to avoid val.
  3. Reliance on Lombok’s Annotation Processing:

    • Lombok’s code manipulation creates a dependency on its annotation processor. While val is a real type and thus valid Java, any changes to how annotation processing works in Java (e.g., compiler internals) could potentially affect how Lombok detects and transforms val declarations.
  4. Future Compatibility Risks:

    • Lombok relies on undocumented/internal APIs of the Java compiler (like com.sun.tools.javac) and integrates deeply with the compilation process. This reliance means that changes in Java’s compiler infrastructure may render val incompatible without updates to Lombok.
  5. Learning Curve:

    • Developers unfamiliar with Lombok may find val counterintuitive or confusing, particularly if they are already accustomed to Java’s var.

Risks for Future Java Versions
#

1. Annotation Processing Policy Changes
#

Future versions of Java could modify how annotation processing works, making Lombok’s AST manipulations more difficult or outright impossible. Discussions around stricter controls on annotation processing have already surfaced in Java’s community.

2. Conflict with Native Features
#

Java already offers var for type inference natively. Introducing further advancements in type inference, or changes to type inference mechanics (e.g., with Project Valhalla), might conflict with Lombok’s val.

3. Dependency on Java Internals
#

Lombok depends on internal compiler APIs like com.sun.tools.javac, which are neither stable nor guaranteed to be supported across major Java versions. Breaking changes could require significant updates to Lombok or make it incompatible until fixed.

4. Inconsistent Support for New Java Versions
#

Historically, Lombok has been slow to support new JDK releases, often requiring updates to remain compatible. This lag might impose delays on projects relying on val when upgrading the Java version.

Should You Use val?
#

Here’s a balanced perspective:

When to Use
#

  • If you are already using Lombok widely and the Java version in your project is below Java 10, val is a natural choice for type inference.

  • Use it in scenarios where variables will remain immutable (final).

When to Avoid
#

  • If you are using Java 10 or above, prefer the native var keyword for type inference.

  • In mutable contexts, avoid val since it enforces final.

Practical Recommendation
#

  • Short-Term (2-5 Years): Lombok’s val is stable and safe to use today in projects already relying on Lombok. However, weigh its usage carefully against var in Java 10+ projects.

  • Long-Term (Beyond 5 Years): Consider migrating towards Java-native features like var for future-proofing. As Java evolves, Lombok’s reliance on annotation processing and compiler internals introduces increasing risk.

Conclusion
#

Lombok’s val remains a compelling choice for modern Java projects—provided you’re already using Lombok and understand its mechanics. It simplifies local variable declarations, promotes immutability, and remains compatible with older Java versions. However, with the rise of native features like var and ongoing changes in the Java platform, the long-term stability of val is not guaranteed. For newly written code, utilizing var is generally more sustainable over time.

By carefully considering your project’s Java version, team familiarity, and potential risks, you can decide whether val deserves a permanent place in your codebase!