Java Lambda

Java 8 Features - Functional Interface, Lambda and Method Reference.

Functional Interface

A functional interface is an interface that contains only one abstract method. @FunctionalInterface annotation is used to ensure that the functional interface can’t have more than one abstract method.

1
2
3
4
5
6
7
8
9
@FunctionalInterface
public interface Runnable {
public abstract void run();
}

@FunctionalInterface
public interface Comparator<T> {
int compare(T o1, T o2);
}

General purpose functional interfaces are defined in java.util.function package.

Lambda Expression

Lambda expression is a block of code you can pass around. It can be viewed as instance of functional interface. Lambda can be assigned to functional interface if the parameter types and return types are matched.

Syntax

1
parameter -> expression body

Lambda Expression Examples:

1
2
3
Predicate<String> isLowercase = (String s) -> {
return StringUtils.isAllUpperCase(s);
};

Type Inference

If the parameter types can be inferred, then you don’t need to provide the parameter types.

1
2
3
Predicate<String> isLowercase = s -> {
return StringUtils.isAllUpperCase(s);
};

Function Body

If the function body consiste of multiple lines, you need to enclose the function body with { }. Otherwise, you can omit the { }

1
Runnable r = () -> System.out.println("Hello World");

If the function body is a return statement only, then return can be omit too.

1
Predicate<String> isLowercase = s -> StringUtils.isAllUpperCase(s);

Usage

Usage 1: lambda expression can be used to replace anonymous inner class. so

1
2
3
4
5
6
btn.setOnAction(new EventHandler<ActionEvent>() {
@Override
public void handle(ActionEvent event) {
System.out.println("Hello World!");
}
});

Can be replaced with

1
btn.setOnAction( event -> System.out.println("Hello World!"));

Usage 2: Use together with Stream to process data.

1
2
3
4
public static void main(String[] args) {
List<String> stringList = Arrays.asList("The", "quick", "brown", "fox", "jumps", "over", "the", "lazy", "dog");
stringList.forEach(s -> System.out.println(s));
}

Variable Capture

Lambda can capture the value of a local variable in the enclosing scope. The captured local variables must be decalred final or effectively final.

However, Lambda can access and modify instance variable of the enclosing class just like anonymous class.

Example:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;

public class HelloWorld extends Application {
private String myInstance = "Foo";

@Override
public void start(Stage primaryStage) {
Button btn = new Button();
btn.setText("Say 'Hello World'");
String myLocal = "Foo";
// myLocal = "Baz"; //Compilation Error: Local variable myLocal defined in an enclosing scope must be final or effectively final
myInstance = "Baz";
btn.setOnAction(event -> {
System.out.println("Hello " + myLocal);
System.out.println("Hello " + myInstance);
});

StackPane root = new StackPane();
root.getChildren().add(btn);

Scene scene = new Scene(root, 300, 250);

primaryStage.setTitle("Hello World!");
primaryStage.setScene(scene);
primaryStage.show();
}

public static void main(String[] args) {
launch(args);
}
}

Here myLocal must be final or effectively final in order for lambda expression to access it. Instance variable myInstance does not have the restriction.

Method Reference

Method reference is a subset of lambda expression. A method reference is the shorthand syntax for a lambda expression
that executes just ONE method. So It is possible to use method reference to replace a lambda expression, but not always.

Method Reference systax

1
ClassName::methodName

See example

1
2
3
4
5
public static void main(String[] args) {
List<String> stringList = Arrays.asList("The", "quick", "brown", "fox", "jumps");
List<String> upperCaseStringList = stringList.stream().map(String::toUpperCase).collect(Collectors.toList());
upperCaseStringList.forEach(System.out::println);
}

Here String::toUpperCase is equivalent to lambda s -> s.toUpperCase()

You can use method reference on constructors

1
2
List<String> ids = Arrays.asList("a12", "b34", "c56");
Stream<Book> bookStream = ids.stream().map(Book::new);

References