Java Lambda Expressions
Lambda expressions, also known as closures, are one of the most significant new features introduced in Java 8.
Lambda allows functions to be passed as parameters to methods (passing functions into methods).
Using Lambda expressions can make the code more concise and compact.
Syntax
The syntax for lambda expressions is as follows:
(parameters) -> expression
or
(parameters) ->{ statements; }
The following are the key features of lambda expressions:
- Optional type declaration: No need to declare parameter types; the compiler can infer the parameter values.
- Optional parentheses for parameters: No need to define parentheses for a single parameter, but multiple parameters require parentheses.
- Optional braces: No need to use braces if the body contains a single statement.
- Optional return keyword: If the body contains a single expression that returns a value, the compiler will automatically return the value; braces are needed to specify that the expression returns a value.
Lambda Expression Examples
Simple examples of lambda expressions:
// 1. No parameters, returns 5
() -> 5
// 2. Takes one numeric parameter, returns its double value
x -> 2 * x
// 3. Takes two numeric parameters, returns their difference
(x, y) -> x - y
// 4. Takes two int parameters, returns their sum
(int x, int y) -> x + y
// 5. Takes a string parameter, prints it to the console, returns no value (similar to returning void)
(String s) -> System.out.print(s)
Enter the following code in the Java8Tester.java file:
Java8Tester.java File
public class Java8Tester {
public static void main(String args[]){
Java8Tester tester = new Java8Tester();
// Type declaration
MathOperation addition = (int a, int b) -> a + b;
// No type declaration
MathOperation subtraction = (a, b) -> a - b;
// Return statement in braces
MathOperation multiplication = (int a, int b) -> { return a * b; };
// No braces and return statement
MathOperation division = (int a, int b) -> a / b;
System.out.println("10 + 5 = " + tester.operate(10, 5, addition));
System.out.println("10 - 5 = " + tester.operate(10, 5, subtraction));
System.out.println("10 x 5 = " + tester.operate(10, 5, multiplication));
System.out.println("10 / 5 = " + tester.operate(10, 5, division));
// No parentheses
GreetingService greetService1 = message ->
System.out.println("Hello " + message);
// With parentheses
GreetingService greetService2 = (message) ->
System.out.println("Hello " + message);
greetService1.sayMessage("tutorialpro");
greetService2.sayMessage("Google");
}
interface MathOperation {
int operation(int a, int b);
}
interface GreetingService {
void sayMessage(String message);
}
}
private int operate(int a, int b, MathOperation mathOperation) {
return mathOperation.operation(a, b);
}
Executing the above script, the output is:
$ javac Java8Tester.java
$ java Java8Tester
10 + 5 = 15
10 - 5 = 5
10 x 5 = 50
10 / 5 = 2
Hello tutorialpro
Hello Google
When using Lambda expressions, note the following:
Lambda expressions are primarily used to define inline execution methods for functional interfaces, such as a simple method interface. In the example above, we used various types of Lambda expressions to define the methods of the MathOperation interface. We then defined the execution of sayMessage.
Lambda expressions eliminate the need for anonymous methods and provide Java with simple yet powerful functional programming capabilities.
Variable Scope
Lambda expressions can only reference outer local variables that are marked as final. This means you cannot modify local variables defined outside the lambda within the lambda, otherwise, a compilation error will occur.
Enter the following code into the Java8Tester.java file:
Java8Tester.java File
public class Java8Tester {
final static String salutation = "Hello! ";
public static void main(String args[]) {
GreetingService greetService1 = message ->
System.out.println(salutation + message);
greetService1.sayMessage("tutorialpro");
}
interface GreetingService {
void sayMessage(String message);
}
}
Executing the above script, the output is:
$ javac Java8Tester.java
$ java Java8Tester
Hello! tutorialpro
We can also directly access outer local variables within the lambda expression:
Java8Tester.java File
public class Java8Tester {
public static void main(String args[]) {
final int num = 1;
Converter<Integer, String> s = (param) -> System.out.println(String.valueOf(param + num));
s.convert(2); // Outputs 3
}
public interface Converter<T1, T2> {
void convert(int i);
}
}
Local variables in lambda expressions do not need to be declared as final, but they must not be modified by subsequent code (implicitly having final semantics).
int num = 1;
Converter<Integer, String> s = (param) -> System.out.println(String.valueOf(param + num));
s.convert(2);
num = 5;
// Error message: Local variable num defined in an enclosing scope must be final or effectively final
Within a Lambda expression, it is not allowed to declare a parameter or local variable with the same name as a local variable in an enclosing scope.
String first = "";
Comparator<String> comparator = (first, second) -> Integer.compare(first.length(), second.length()); // Compilation error