- Download and Installation
- Getting Started with Lambda Expressions
Getting Started with Lambda Expressions
JSR 335 is large and still being developed, so I wanted to choose something to demonstrate lambda expressions that shows the value they offer and as well as the syntax, which isn't likely to change much in the next few months. In this section, I'll demonstrate how to use lambda expressions for functional interfaces and how to pass lambda expressions to new methods in the collection classes to perform operations on the contents of the collection.
For the first example, consider the standard mechanism in Java 7 for sorting a collection of objects: To sort, you need to create a class that implements the Comparable interface and overrides the compareTo() method. This is pretty straightforward, and you've probably done it dozens of times, but it's a bit cumbersome. Listing 1 shows how we can do this to sort a list of Strings.
Listing 1: TraditionalComparatorTest.java.
package test; import java.util.*; public class TraditionalComparatorTest { public static void showList( List<String> list ) { System.out.println( "List: " ); for( String element : list ) { System.out.println( "\t" + element ); } } public static void main( String[] args ) { List<String> myList = new ArrayList<String>(); // Build a list myList.add( "Z" ); myList.add( "A" ); myList.add( "M" ); showList( myList ); // Sort the list using a closure Collections.sort( myList, new Comparator<String>() { @Override public int compare( String a1, String a2 ) { return a1.compareTo( a2 ); } } ); showList( myList ); } }
In Listing 1, we create an anonymous inner class that implements the compare() method, and we pass that to the Collections.sort() method. The output from running this class is shown below:
List: Z A M List: A M Z
Listing 2 shows how we can accomplish the same thing by using a lambda expression that we can pass directly to the Collections.sort() method.
Listing 2: LambdaComparatorTest.java.
package test; import java.util.*; public class LambdaComparatorTest { public static void showList( List<String> list ) { System.out.println( "List: " ); for( String element : list ) { System.out.println( "\t" + element ); } } public static void main( String[] args ) { System.out.println( "Hello, Lambda" ); List<String> myList = new ArrayList<String>(); // Build a list myList.add( "Z" ); myList.add( "A" ); myList.add( "M" ); showList( myList ); // Sort the list using a closure Collections.sort( myList, ( String a1, String a2 ) -> ( a1.compareTo( a2 ) ) ); showList( myList ); } }
The lambda expression is constructed by specifying the input variables (two Strings), followed by an arrow, and then the expression to execute, enclosed in parentheses:
( String a1, String a2 ) -> ( a1.compareTo( a2 ) )
Anywhere you have a functional interface, you can pass in a lambda expression in its stead.
In addition to functional interfaces, Project Lambda adds new functionality to collections. Listing 3 and listing 4 demonstrate how to filter a list of objects based on an object value and then how to sort those items by an arbitrary field.
Listing 3: Book.java.
package test; public class Book { private String author; private String title; private String genre; private int year; public Book() { } public Book( String author, String title, String genre, int year ) { this.author = author; this.title = title; this.genre = genre; this.year = year; } public String getAuthor() { return author; } public void setAuthor( String author ) { this.author = author; } public String getTitle() { return title; } public void setTitle( String title ) { this.title = title; } public String getGenre() { return genre; } public void setGenre( String genre ) { this.genre = genre; } public int getYear() { return year; } public void setYear( int year ) { this.year = year; } public String toString() { return title + " written by " + author + " in the year " + year + " is of the genre: " + genre; } }
Listing 4: LambdaCollectionTest.java.
package test; import java.util.*; public class LambdaCollectionTest { public static void showBooks( List<Book> list ) { System.out.println( "Book List: " ); for( Book book : list ) { System.out.println( "\t" + book ); } } public static void main( String[] args ) { // Build our set of books List<Book> list = new ArrayList<Book>(); list.add( new Book( "Author A", "Title 1", "Fiction", 1990 ) ); list.add( new Book( "Author C", "Title 3", "Fiction", 1995 ) ); list.add( new Book( "Author B", "Title 2", "Fiction", 1980 ) ); list.add( new Book( "Author E", "Title 6", "Computer Science", 2000 ) ); list.add( new Book( "Author D", "Title 5", "Computer Science", 2010 ) ); list.add( new Book( "Author F", "Title 7", "Computer Science", 2000 ) ); showBooks( list ); // Filter on Fiction Books List<Book> fictionBooks = list.filter( b -> b.getGenre().equals( "Fiction") ) .into( new ArrayList<Book>() ); showBooks( fictionBooks ); // Filter on Fiction Books and sort by year List<Book> sortedFictionBooks = list.filter( b -> b.getGenre().equals( "Fiction") ) .sorted( ( Book b1, Book b2 ) -> { if( b1.getYear() > b2.getYear() ) return 1; else if( b1.getYear() == b2.getYear() ) return 0; else return -1; } ) .into( new ArrayList<Book>() ); showBooks( sortedFictionBooks ); } }
Listing 3 presents a simple POJO for a book object. Listing 4 shows three new methods added to the java.lang.Iterable interface:
- filter(): The filter() method allows you to filter a collection by the value contained in one (or more) of the object's fields. Listing 4 retrieves the Book's genre and only includes those books with a genre of Fiction.
- into(): When you invoke a method like filter(), it returns an instance of Iterable, and you probably want to do something with the results of the filter. The into() method allows you to create a new collection into which the results of all expressions will be copied.
- sorted(): The sorted() method allows you to provide a Comparator implementation—the same as the Collections.sort() method—with which to sort your list.
The first example shows how to filter only on fiction books and then create a new list that contains the filtered books. The second example expands on that logic to include a sorting function that sorts by date.
The filter expression implicitly passes each instance in your list and allows you to return a Boolean result indicating whether it should be included. While the following expression filters only on Fiction, you can make it more complex:
b -> b.getGenre().equals( "Fiction")
On the other hand, you can pass the filtered list to another filter, which might make it read cleaner (for example, filter on "Fiction" and then on years greater than 1995).
As I mentioned earlier, the sorted() method accepts a Comparator functional expression. Strings are easy to compare, as we did earlier in this article, because the String class already provides a compareTo() method. However, to compare integers (and to illustrate how to build blocks of expressions), the expression needs to examine the individual values of the year field and return -1, 0, or 1, as the Comparator.compare() method specifies:
( Book b1, Book b2 ) -> { if( b1.getYear() > b2.getYear() ) return 1; else if( b1.getYear() == b2.getYear() ) return 0; else return -1; }
Summary
This article has only touched the surface of lambda expressions, but with the release of Java 8 scheduled for summer 2013, you have time to get ready. Fortunately, Project Lambda provides a prototype that you can use to experiment. The only warning I can give you is that many of the examples in the documentation don't match the code implementation—so it might be a good idea to unzip the src.zip file in the prototype to find out why things aren't working as you think.
In terms of language enhancements, Project Lambda aims to make things easier and cleaner for Java developers, and when you explore more of the advanced features, you'll see that it provides some new paradigms to help you solve problems that are tedious today.