Java 11 var in Lambda Parameters with Examples

Introduction Java 10 introduced the var keyword for local variables. Java 11 (JEP 323) extended it to the parameters of lambda expressions. This tutorial explains var in lambda parameters in Java 11 with examples. We will first recap the two ways of declaring lambda parameters which already existed, and see what the var form adds. We will then see the main reason the feature was added - putting annotations and modifiers such as final on lambda parameters. Lastly, we will go through the rules the compiler enforces, along with the errors it gives when they are broken.

(Note - This tutorial assumes you know lambda expressionsClick to read: lambda expressions and the var keyword for local variablesClick to read: var keyword for local variables.) Three ways to declare lambda parameters Since Java 8, the parameters of a lambda could either be explicitly typed, or implicitly typed, in which case the compiler infers their types from the functional interface. Java 11 adds a third way, which uses var -
Explicit, implicit and var lambda parameters
//LambdaParamStyles.java
import java.util.function.BiFunction;

public class LambdaParamStyles {
  public static void main(String[] args) {
    // 1. Explicitly typed (Java 8)
    BiFunction<String, Integer, String> explicit = (String s, Integer n) -> s.repeat(n);
    // 2. Implicitly typed (Java 8)
    BiFunction<String, Integer, String> implicit = (s, n) -> s.repeat(n);
    // 3. var (Java 11)
    BiFunction<String, Integer, String> withVar = (var s, var n) -> s.repeat(n);

    System.out.println(explicit.apply("ab", 2));
    System.out.println(implicit.apply("cd", 2));
    System.out.println(withVar.apply("ef", 2));
  }
}
 OUTPUT of the above code
abab
cdcd
efef
Explanation of the code
  • All three lambdas compile to exactly the same thing. With var, the types of s and n are inferred as String and Integer from BiFunction<String, Integer, String>, just as they are for the implicitly typed lambda.
  • So, on its own, (var s, var n) is only a longer way of writing (s, n). The reason to use it is explained next.
Why var was added: annotations and modifiers Annotations and modifiers can only be written on a parameter that has a type in front of it. An implicitly typed parameter such as s has nowhere to put them, so before Java 11 the only option was to write out the full type, which can be long. var gives you a place to put the annotation while the compiler still infers the type -
Annotations and final on lambda parameters
//AnnotatedLambda.java
import java.lang.annotation.ElementType;
import java.lang.annotation.Target;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import java.util.function.BiConsumer;

public class AnnotatedLambda {

  @Target(ElementType.PARAMETER)
  @interface NonNull { }

  public static void main(String[] args) {
    Map<String, List<Integer>> marks = new TreeMap<>(Map.of(
        "Tom", List.of(78, 91), "Harry", List.of(85, 64)));

    // Without var, annotating the parameters needs the full types:
    BiConsumer<String, List<Integer>> before =
        (@NonNull String name, @NonNull List<Integer> scores) -> System.out.println(name + " " + scores);

    // With var, the types are inferred and the annotations still fit:
    BiConsumer<String, List<Integer>> after =
        (@NonNull var name, final var scores) -> System.out.println(name + " averaged "
            + scores.stream().mapToInt(Integer::intValue).average().orElse(0));

    marks.forEach(before);
    marks.forEach(after);
  }
}
 OUTPUT of the above code
Harry [85, 64]
Tom [78, 91]
Harry averaged 74.5
Tom averaged 84.5
Explanation of the code
  • @NonNull is a simple annotation which can be placed on parameters. In real projects you would use an annotation from a library such as the Checker Framework or JSpecify, which tools then use to check for nulls.
  • In before, the annotations forced us to write String and List<Integer>. In after, @NonNull var name keeps the annotation but lets the compiler infer the type.
  • final var scores makes the parameter final, so assigning to scores inside the lambda would be a compile error. This was also not possible with an implicitly typed parameter.
  • var also makes lambda parameters consistent with local variables, which can be declared with var since Java 10.
Rules for using var in lambda parameters
  • All or nothing - if one parameter uses var, all of them must. You cannot mix var with explicitly typed or implicitly typed parameters.
  • Parentheses are required - a single implicitly typed parameter can be written without parentheses, as in s -> s.length(), but var s -> s.length() is not allowed. Write (var s) -> s.length().
  • Only in lambdas - var is still not allowed for the parameters of methods and constructors.
The following lambdas break the first rule -
Mixing var with other parameter styles
//VarLambdaMixing.java
import java.util.function.BiFunction;

public class VarLambdaMixing {
  BiFunction<String, Integer, String> mixImplicit = (var s, n) -> s.repeat(n);
  BiFunction<String, Integer, String> mixExplicit = (var s, Integer n) -> s.repeat(n);
}
 OUTPUT on compiling the above code
VarLambdaMixing.java:5: error: invalid lambda parameter declaration
  BiFunction<String, Integer, String> mixImplicit = (var s, n) -> s.repeat(n);
                                                    ^
  (cannot mix 'var' and implicitly-typed parameters)
VarLambdaMixing.java:6: error: invalid lambda parameter declaration
  BiFunction<String, Integer, String> mixExplicit = (var s, Integer n) -> s.repeat(n);
                                                    ^
  (cannot mix 'var' and explicitly-typed parameters)
2 errors
And this one breaks the second rule -
var without parentheses
//VarLambdaParens.java
import java.util.function.Function;

public class VarLambdaParens {
  Function<String, Integer> length = var s -> s.length();
}
 OUTPUT on compiling the above code
VarLambdaParens.java:5: error: ';' expected
  Function<String, Integer> length = var s -> s.length();
                                        ^
VarLambdaParens.java:5: error: <identifier> expected
  Function<String, Integer> length = var s -> s.length();
                                          ^
VarLambdaParens.java:5: error: <identifier> expected
  Function<String, Integer> length = var s -> s.length();
                                                      ^
3 errors
The errors for this one are less helpful. Without parentheses, the compiler does not see a lambda at all - it reads var s as the start of a variable declaration, and reports the syntax errors which follow from that. If you see ';' expected pointing at a lambda, check for a var parameter without parentheses.
When to use var in lambda parameters Implicitly typed parameters such as (s, n) -> ... remain the shortest and most readable form, and are the right choice for most lambdas. Use var when you need to add an annotation or the final modifier to a lambda parameter. Use explicit types when the type is not obvious and writing it makes the code easier to read. Summary In this tutorial we saw that Java 11 allows var for lambda parameters, in addition to the explicitly typed and implicitly typed forms of Java 8. We saw that the main use of var here is to add annotations and final to lambda parameters without writing their full types. Lastly, we went through the rules - var must be used for all the parameters or none, and the parameters must be in parentheses - along with the compiler errors for breaking them. In the next tutorial, we will see how Java 11 can run single-file source code programsClick to read tutorial: Java 11: Run Single-File Source Code Programs with java directly with the java command.