dev only

Lambdas & Method References

The Problem Lambdas Solve

When an interface declares exactly one abstract method (a functional interface), implementing it with a full named class — or even an anonymous class — is a lot of ceremony for something conceptually simple: "here's the one bit of behavior you asked for." A lambda expression lets you write that one method's body directly, with no class declaration at all:

public interface DoubleSupplier
{
    double getAsDouble(); // exactly one abstract method — this is a functional interface
}
// Without a lambda: a full anonymous class just to supply one value
DoubleSupplier speed = new DoubleSupplier()
{
    @Override
    public double getAsDouble() { return joystick.getY(); }
};

// With a lambda: the same thing, far more directly
DoubleSupplier speed = () -> joystick.getY();

WPILib commands and suppliers are built almost entirely on this pattern — this is exactly what makes lambdas so central to command-based programming (Ch.25).

Lambda Syntax

A lambda expression has three parts: a parameter list in parentheses, the arrow ->, and a body:

(parameters) -> expression
  • Parameter types are inferred, not written — the compiler already knows them from the functional interface being implemented.
  • Parentheses can be dropped around a single parameter: p -> p.getY() is valid, just like (p) -> p.getY().
  • A single expression is the return value automatically — no return keyword needed. A multi-statement body needs braces and an explicit return:
DoubleSupplier speed = () -> joystick.getY();              // single expression — auto-returned

DoubleSupplier clampedSpeed = () -> {                       // multi-statement body needs braces + return
    double raw = joystick.getY();
    return Math.max(-1.0, Math.min(1.0, raw));
};

A lambda with more than one parameter lists them comma-separated inside the parentheses:

public interface IntBinaryOperation
{
    int apply(int a, int b);
}

IntBinaryOperation add = (a, b) -> a + b;

Common Functional Interfaces You'll Actually Use

Java (and WPILib) ship several ready-made functional interfaces, so you rarely need to declare your own:

Runnable stopAll = () -> intake.stop();                 // no arguments, no return value
DoubleSupplier speed = () -> joystick.getY();            // no arguments, returns a double
BooleanSupplier hasGamePiece = () -> intake.hasPiece();  // no arguments, returns a boolean

Method References: An Even Shorter Form

When a lambda's entire body is just calling one existing method, a method reference (::) says the same thing even more directly:

Runnable stopAll = () -> intake.stop(); // a lambda...
Runnable stopAll2 = intake::stop;       // ...is exactly equivalent to this method reference

intake::stop reads as "the stop method, called on intake" — Java fills in the call automatically, with whatever arguments the functional interface's method expects. Method references exist purely as a shorthand for a lambda that does nothing but forward to an existing method — anywhere a method reference works, the equivalent lambda would work identically.

Common Pitfalls

  • Writing parameter types into a lambda. (Person p) -> p.getAge() — while technically legal — is unusual style; letting the compiler infer the type (p -> p.getAge()) is the normal convention, since the functional interface already pins down what type p has to be.
  • Forgetting return in a multi-statement lambda body. A single-expression lambda auto-returns its value, but a { } block body needs an explicit return, exactly like a regular method.
  • Reaching for a method reference when the lambda does more than forward one call. () -> { intake.stop(); logEvent("stopped"); } genuinely needs a lambda — a method reference only replaces a lambda that does nothing but call one existing method.

Key Takeaways

  • A lambda expression is a compact way to implement a functional interface (an interface with exactly one abstract method) inline, without writing a class.
  • Syntax: (parameters) -> expression (or { statements; return value; } for a multi-statement body); parameter types are inferred, and parentheses can be dropped for a single parameter.
  • Runnable, DoubleSupplier, and BooleanSupplier are common ready-made functional interfaces WPILib code uses constantly.
  • A method reference (object::method) is exact shorthand for a lambda that does nothing but call one existing method.

Derived from other-reference-repo: oracle-java-tutorials/lambdas/lambda-expressions.md (ORACLE 12.2) Deck context: mechacoder-test/src/lessons/java-1.js, slide 12 ("Lambdas & Method References")