Back to Blog
Java

Using var with Lambda Expressions in Java

java var with lambda: Explains how var interacts with lambda expressions in Java: why var cannot hold a lambda, when var works in lambda parameters, and the all-or-not...

JavaLambda Expressionsvar KeywordType InferenceJava 11
Illustration of the Java var keyword interacting with a lambda expression, showing the type inference boundary.

When Java developers search for java var with lambda, the question usually involves one of two distinct syntax rules: whether var can hold a lambda expression, and whether var can appear inside a lambda's parameter list. Both rules exist, and they behave very differently.

Why var Cannot Hold a Lambda Expression Directly

var relies on the compiler inferring the variable's type from its initializer. For most expressions that works because the expression has a well-defined type. A lambda expression does not. A lambda like x -> x * 2 has no type of its own; it only becomes a Function<Integer, Integer>, a UnaryOperator<Integer>, or any other compatible functional interface when the compiler has a target type to work against.

var doubler = x -> x * 2; // compilation error

The compiler cannot infer a functional interface type for doubler because nothing in the initializer narrows the lambda to a specific target type. This fails with a "cannot infer type" style error. The fix is to declare the variable with an explicit functional interface type:

UnaryOperator<Integer> doubler = x -> x * 2;

or to pass the lambda to a method or constructor whose parameter type provides the target type. This is the most common mistake developers make when first combining var with lambdas, and it is worth understanding before moving to the parameter-list case.

Using var in Lambda Parameters

Since Java 11, var is allowed in lambda parameter lists. This is the feature introduced by JEP 323. It lets you write:

BiFunction<Integer, Integer, Integer> sum = (var a, var b) -> a + b;

The compiler still infers the parameter types from the target functional interface. var here does not change the inferred types; it only changes the syntax. The types of a and b are Integer because the target type is BiFunction<Integer, Integer, Integer>.

The primary motivation for this feature is annotations. Before Java 11, you could not apply a type annotation to a lambda parameter without writing the full type explicitly. With var, you can annotate the parameter while leaving the type inferred:

BiFunction<String, String, String> join = (@Nullable var left, @NotNull var right) -> left + right;

assuming @Nullable and @NotNull are annotation types available in your project. This is the main practical reason to use var in a lambda parameter list. If you have no annotations to apply, plain inferred parameters are shorter and clearer.

The All-or-Nothing Rule

When you use var in a lambda parameter list, every parameter must use var. You cannot mix explicit types with var:

BiFunction<Integer, Integer, Integer> sum = (var a, Integer b) -> a + b; // error

This is a hard syntax rule, not a style preference. If you need one parameter to have an explicit type, you must write all parameters explicitly. The rule exists because the compiler treats the parameter list as either fully inferred or fully explicit, and mixing the two would make annotation semantics ambiguous.

What var Does Not Change

It is important to be precise about what var in a lambda parameter does not do:

  • It does not change the inferred parameter types.
  • It does not make the lambda polymorphic.
  • It does not allow the same lambda to target multiple functional interfaces.
  • It does not affect the lambda's runtime behavior.

The bytecode generated for (var a, var b) -> a + b is identical to the bytecode for (Integer a, Integer b) -> a + b when both target the same functional interface. var is purely a compile-time syntax feature with no runtime cost.

When var in Lambda Parameters Is Actually Useful

Beyond annotations, var in lambda parameters is useful in a few narrow cases:

  • When a parameter type is long and generic-heavy, var can improve readability, though this is subjective.
  • When you want to apply a type annotation without repeating the full type.
  • When you are refactoring and the parameter type may change, var reduces the number of places that need editing.

For most lambda expressions, plain inferred parameters remain the better choice. var adds characters without adding information when no annotations are present.

Compatibility and Maintainability

var in lambda parameters requires Java 11 or later. If your codebase targets Java 10 or earlier, this syntax will not compile. var for local variables requires Java 10. Teams on older LTS versions should verify their toolchain before adopting the syntax.

From a maintainability perspective, var in lambda parameters is most defensible when annotations are involved. Without annotations, the explicit form is often clearer because it documents the contract at the call site. There is also a subtle readability concern: when a reader sees (var a, var b), they must look up the functional interface type to know what a and b actually are. That indirection is acceptable in short lambdas but becomes a burden in longer ones.

The general guidance is straightforward: use var in lambda parameters when you need annotations, and prefer plain inferred parameters otherwise. Never try to use var to declare a lambda variable without a target type, because the language does not support it.

java var with lambda: Syntax Rules and Limits | RYUSLOG DEV