Skip to content

The :: shortcut, static and object methods

โ˜• Java 17 ยท Passing Methods Around by Name ยท 1/3

When a lambda only passes the baton

interface TitleAnnouncer {
    void announce(String title);
}

TitleAnnouncer a = title -> System.out.println(title);   // lambda
TitleAnnouncer b = System.out::println;                  // method reference

b.announce("Stranger Things");

The lambda receives title and hands it, untouched, to println. The method reference System.out::println says it directly: when announce() is called, call println with the same argument.

Like a lambda, it runs nothing when declared: println runs only when announce() is called.

One shortcut, four shapes

# Shape Reference Equivalent lambda
1 static method Math::round x -> Math.round(x)
2 method on an object title::startsWith s -> title.startsWith(s)
3 method on the parameter String::isEmpty t -> t.isEmpty()
4 constructor Episode::new n -> new Episode(n)

A method reference behaves exactly like its lambda. It has no type of its own: like a lambda, it always fills a functional interface, whose parameters and return type tell Java which method to call.

Shape 1 โ€” A static method

interface PriceRounder {
    long round(double price);
}

PriceRounder ref = Math::round;            // same as p -> Math.round(p)
System.out.println(ref.round(15.49));      // 15
  • Syntax: ClassName::method, with no parentheses and no arguments.
  • The interface's parameter becomes the method's parameter.
  • Math.round exists for double and float; the interface takes a double, so Java picks that one. No match, or two equally good matches, is a compile error.

Shape 2 โ€” A method on an object you have

interface TitleMatcher { boolean beginsWith(String prefix); }
interface TitleCheck { boolean check(); }

var title = "Stranger Things";

TitleMatcher ref = title::startsWith;      // prefix -> title.startsWith(prefix)
System.out.println(ref.beginsWith("Str")); // true

TitleCheck empty = title::isEmpty;         // () -> title.isEmpty()
System.out.println(empty.check());         // false

The left side is a real object, here the variable title. The method always runs on that object; the interface's parameters become its arguments, even zero of them.

When a lambda can't become a reference

TitleCheck c1 = title::startsWith("Str");    // โŒ no arguments after ::
TitleCheck c2 = title::startsWith;           // โŒ check() has nothing to pass as a prefix
TitleCheck c3 = () -> title.startsWith("Str"); // โœ… keep the lambda

A method reference is only a name: it can't carry fixed values or extra logic. It's a one-way street: every method reference can be rewritten as a lambda, but not every lambda as a method reference.

Shapes 3 and 4 are the trickier ones: the next page reads them step by step.

๐Ÿ’ป Code: Java17_Part_8_Functional_Programming on GitHub

Next: the parameter and constructor shapes โ†’