Java 8: Lambda Expression Basics

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:

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.