Encodera

Thanks for reading!

Donate to the site — we work hard for you.

Contents

Method references

Often a lambda just calls one existing method and passes its parameter to it: s -> s.toUpperCase(), x -> IO.println(x). Such a lambda can be written shorter — as a method reference with the :: operator.

The first method reference

To the left of :: goes a class or an object, to the right the method name without parentheses. Both lines below do the same thing:

void main() {
    List<String> names = List.of("Anna", "Oleg");
    names.forEach(name -> IO.println(name));
    names.forEach(IO::println);
}

Output:

Anna
Oleg
Anna
Oleg

The reference IO::println means "call IO.println() with each element". Java fills in the arguments by itself. A method reference is not a call: the method runs later, when forEach() calls it.

A static method

A reference of the form ClassName::staticMethod passes the lambda's parameters to a static method. This is handy for turning strings into numbers:

void main() {
    Function<String, Integer> parse = Integer::parseInt;
    BinaryOperator<Integer> add = Integer::sum;
 
    IO.println(parse.apply("42") + 1);
    IO.println(add.apply(2, 3));
}

Output:

43
5

A method of an arbitrary object

The notation String::toUpperCase looks like a static one but works differently: the lambda's first parameter becomes the object on which the method is called. It is equivalent to s -> s.toUpperCase():

void main() {
    Function<String, String> upper = String::toUpperCase;
    Function<String, Integer> length = String::length;
 
    IO.println(upper.apply("hello"));
    IO.println(length.apply("hello"));
}

Output:

HELLO
5

This is exactly how the comparators from the chapter on collections work: Comparator.comparing(Product::price) calls price() on each product.

A method of a particular object

If a variable stands on the left, the method is called on that object, and the lambda's parameter becomes the argument:

void main() {
    List<String> cart = new ArrayList<>();
    Consumer<String> addToCart = cart::add;
 
    addToCart.accept("tea");
    addToCart.accept("cookies");
    IO.println(cart);
}

Output:

[tea, cookies]

A constructor reference

ClassName::new is a reference to a constructor. It creates a new object from the lambda's parameters:

record User(String name) {}
 
void main() {
    Function<String, User> create = User::new;
    Supplier<List<String>> newList = ArrayList::new;
 
    IO.println(create.apply("Anna"));
    IO.println(newList.get());
}

Output:

User[name=Anna]
[]

Common mistake

A method reference fits only if the method's parameters and return type match the functional interface. For example, String::length returns a number, while Predicate expects a boolean:

void main() {
    Predicate<String> check = String::length;
}

Output:

Main.java:2: error: incompatible types: bad return type in method reference
    Predicate<String> check = String::length;
                              ^
    int cannot be converted to boolean

If the method does not fit directly, write an ordinary lambda: s -> s.length() > 3. In general, a method reference is not an obligation but a shortcut: pick whichever form reads more easily.

Thanks for reading!

Donate to the site — we work hard for you.