This article covers the basic syntax of lambda expressions included in Java 8. The goal is to provide an introduction to Lambda expressions and how to use them.
Setting up the Environment
To run the examples in this article, you must have a version of JDK 8 with Lambda support and a copy of NetBeans 8 with Lambda Support. Links can be found at the main Lambda site. For you convenience, here are the direct links.
Background
Anonymous Inner Class
In Java, anonymous inner classes provide a way to implement classes that may only occur once in an application. For example, in a standard Swing or JavaFX application a number of event handlers are required for keyboard and mouse events. Rather than writing a separate event handling class for each event, something like this is written.
21
22 JButton testButton = new JButton("Test Button");
23 testButton.addActionListener(new ActionListener(){
24 public void actionPerformed(ActionEvent ae){
25 System.out.println("Click Detected by Anon Class");
26 }
27 });
28
Otherwise, a separate class that implements ActionListener
would be required for each event. By creating the class in place where it is needed, this makes the code a little easier to read. But the code is not exactly elegant. There is quite a bit of code required just to define one method.
Functional Interfaces
If you look at the code that defines the ActionListener
interface, it would look something like this.
1 package java.awt.event;
2 import java.util.EventListener;
3
4 public interface ActionListener extends EventListener {
5
6 public void actionPerformed(ActionEvent e);
7
8 }
This example is an interface
with only one method. Moving forward, an interface
that follows this pattern is know as a "functional interface."
Note: This type of
interface
, was previously known as a SAM type (Single Abstract Method).
Using functional interfaces with anonymous inner classes turns out to be a pretty common pattern in Java. In addition to the EventListner
classes, interfaces like Runnable
and Comparator
are used in a similar manner. Therefore, functional interfaces are leveraged for use with lambda expressions.
Lambda Expression Syntax
Lambda expressions address the bulkiness of anonymous inner classes by converting 5 lines of code into a single statement. This solves the "veritcal problem" presented by inner classes with a more simple horizontal solution.
A lambda expression is composed of three parts.
Argument List | Arrow Token | Body |
(int x, int y) |
-> |
x + y |
The body can either be a single expression or a statement block. In the expression form, the body is simply evaluated and returned. In the block form, the body is evaluated like a method body a return statement returns control to the caller of the anonymous method; break
and continue
are illegal at the top level, but are of course permitted within loops; and if the body produces a result, every control path must return something or throw an exception.
Here are a few examples to review:
(int x, int y) -> x + y () -> 42 (String s) -> { System.out.println(s); }
The first expression takes two integer arguments, named x
and y
, and uses the expression form to returns x+y
. The second takes no arguments and using the expression form to return an integer 42. The third takes a string and uses the block form to print the string to the console, returning nothing.
Lambda Examples
So with the basic syntax explained, we can review a few common use cases.
Runnable Lambda
How can you write a Runnable
using lambdas? Like this:
6 public class RunnableTest {
7 public static void main(String[] args) {
8
9 System.out.println("=== RunnableTest ===");
10
11 // Anonymous Runnable
12 Runnable r1 = new Runnable(){
13
14 @Override
15 public void run(){
16 System.out.println("Hello world one!");
17 }
18 };
19
20 // Lambda Runnable
21 Runnable r2 = () -> { System.out.println("Hello world two!"); };
22
23 // Run em!
24 r1.run();
25 r2.run();
26
27 }
28 }
Notice that in both cases, there is no parameter passed and nothing to return. The Runnable
lambda expression, which uses the block format, converts 5 lines of code into 1 statement.
Comparator Lambda
The Comparator
class is used in Java for sorting collections. In the example that follows, an ArrayList
of the Person
class will be sorted based on surName
. Here are the fields included in the person class.
9 public class Person {
10 private String givenName;
11 private String surName;
12 private int age;
13 private Gender gender;
14 private String eMail;
15 private String phone;
16 private String address;
17
The following code applies a Comparator
using an anonymous inner class and a couple lambda expressions.
10 public class ComparatorTest {
11
12 public static void main(String[] args) {
13
14 List<Person> personList = Person.createShortList();
15
16 // Sort with Inner Class
17 Collections.sort(personList, new Comparator<Person>(){
18 public int compare(Person p1, Person p2){
19 return p1.getSurName().compareTo(p2.getSurName());
20 }
21 });
22
23 System.out.println("=== Sorted Asc SurName ===");
24 for(Person p:personList){
25 p.printName();
26 }
27
28 // Use Lambda instead
29 Comparator<Person> SortSurNameAsc = (Person p1, Person p2) -> p1.getSurName().compareTo(p2.getSurName());
30 Comparator<Person> SortSurNameDesc = (p1, p2) -> p2.getSurName().compareTo(p1.getSurName());
31
32 // Print Asc
33 System.out.println("=== Sorted Asc SurName ===");
34 Collections.sort(personList, SortSurNameAsc);
35
36 for(Person p:personList){
37 p.printName();
38 }
39
40 // Print Desc
41 System.out.println("=== Sorted Desc SurName ===");
42 Collections.sort(personList, SortSurNameDesc);
43
44 for(Person p:personList){
45 p.printName();
46 }
47
48 }
49 }
Lines 17 - 21 are easily replaced by the lambda expressions on lines 29 and 30. Notice that the first lambda expression declares the parameter passed to the expression. But as you can see from the second expression, this is optional. Lambda Supports "target typing" which infers the object type from the context in which it is used. Since we are assigning the result to a Comparator
defined with a generic, the compiler can infer the two parameters are of the Person
type.
Listener Lambda
Finally, we can revisit the ActionListener
example from earlier. Note: The example uses SWing but will probably updated to JavaFX at some point.
19 public class ListenerTest {
20 public static void main(String[] args) {
21
22 JButton testButton = new JButton("Test Button");
23 testButton.addActionListener(new ActionListener(){
24 public void actionPerformed(ActionEvent ae){
25 System.out.println("Click Detected by Anon Class");
26 }
27 });
28
29 testButton.addActionListener(e -> {
30 System.out.println("Click Detected by Lambda Listner");
31 });
32
33 // Swing stuff
34 JFrame frame = new JFrame("Listener Test");
35 frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
36 frame.add(testButton, BorderLayout.CENTER);
37 frame.pack();
38 frame.setVisible(true);
39
40 }
41 }
Notice in this example, the lambda expression is passed as a parameter. This highlights that the target typing can be used in a number of contexts. The contexts include:
- Variable declarations
- Assignments
- Return statements
- Array initializers
- Method or constructor arguments
- Lambda expression bodies
- Conditional expressions ?:
- Cast expressions
Summary
To sum up, you have learned about lambda expression syntax and seen their use in some common use cases. But we are just scratching the surface, more to come in future posts.
Reference
Code: Click here to download all the examples and source code. (Size: Aprox 18k)
Note: For authoritative coverage of lambda topics please see Brian Goetz' State of the Lamba document.