If an interface has a single abstract method, it’s called a functional interface. By default, all
methods in an interface are public and abstract. You can use the @FunctionalInterface
annotation to specify that your interface will always have a single abstract method — if you add
another non-default method, you’ll get an error.
@FunctionalInterfaceinterface MyLambda { public void display();}You can implement this interface and override display:
class My implements MyLambda { @Override public void display() { System.out.println("Hello World"); }}But there’s a neater way, using lambda expressions. Instead of creating a whole new class that
implements MyLambda, we create an instance/reference of the interface directly, and use a
lambda expression to override display.
public class LambdaDemo { public static void main(String[] args) { // the below is for methods taking no params -> empty brackets MyLambda m = () -> { System.out.println("Hello World"); }; m.display(); }}We create a reference of MyLambda directly, use empty brackets () to represent display’s
parameters, an arrow -> to show what the method does, then the method body. Calling
m.display() runs it.
Lambda expressions taking parameters
If the method being overridden takes a single parameter:
@FunctionalInterfaceinterface MyLambda { public void display(String str);}Include the parameter inside the brackets of the lambda expression, and specify its value when you call the method:
public class LambdaDemo { public static void main(String[] args) { MyLambda m = (s) -> { // pass the parameter in the brackets System.out.println(s); }; m.display("Hello World"); // specify the contents of the parameter }}Multiple parameters work the same way:
@FunctionalInterfaceinterface MyLambda { public int add(int x, int y);}
public class LambdaDemo { public static void main(String[] args) { MyLambda m1 = (a, b) -> a + b; MyLambda m2 = (int a, int b) -> a + b; // another valid option, with explicit types
int result = m1.add(20, 30); System.out.println(result); }}Lambda expressions can have their own local variables, and use them as much as needed.
Lambda expressions can only access or capture local variables if those variables are final, or never modified after being assigned (effectively final). Lambdas can access or modify instance variables freely — those don’t need to be final. In this sense, lambda expressions behave a lot like inner classes.
When a method takes a functional interface as a parameter, you can pass a lambda expression directly as the argument:
@FunctionalInterfaceinterface MyLambda { public void display();}
class UseLambda { public void callLambda(MyLambda ml) { ml.display(); }}
class Demo { public void method1() { UseLambda ul = new UseLambda(); ul.callLambda(() -> System.out.println("Hello")); }}What happens on the right side of the arrow
So far the examples have focused on the left side of -> (the parameters). The right side has a
few forms too.
public interface Z { void m();}Ways to use lambdas:
- Return a value directly from the lambda.
- Return a reference to a method that returns the same type as the functional interface’s method — this doesn’t call the method immediately; it’s only called when the interface’s method is actually invoked.
- Use the
returnkeyword inside curly braces (a normal code block). In most cases you’re better off writing a named method and passing a reference to it (option 2 above) rather than a multi-statement lambda body.
public class Example { static int q() { return 4; }
public static void main(String[] args) { Z z1 = () -> 5; // example 1: return a value directly
Z z2 = () -> q(); // example 2: q() hasn't been called yet z2.m(); // now q() gets called
Z z3 = () -> { // example 3: a normal code block int e = 10; return e; }; }}