Do more with less: Lambda expressions in Java 8

Using lambda expressions can make your Java code leaner, more powerful, and easier to read

One of the recurrent, valid criticisms of Java as a programming language is that it is wordy. You have to write a lot of code to get anything done. Lambda expressions relieve that problem for some common cases and give Java useful facilities that have boosted rival C# for years.

Lambda expressions are new to Java SE 8, and in my opinion they are the most significant language features debuting in this version. Lambda expressions provide a relatively clear and more concise way to represent a method interface using an expression. Lambda expressions also improve the Java Collection libraries, making it easier to iterate through, filter, and extract data from Collections. New concurrency features enabled by lambda expressions improve Java runtime performance in multicore environments, which applies to the vast majority of new devices and computers.

[ Nashorn: JavaScript made great in Java 8 | 15 things we hate about Java | Keep up with the latest developer news with InfoWorld's Developer World newsletter. ]

I've heard programmers express the fear that lambda expressions will pollute the object-oriented nature of Java with functional programming constructs. I heard similar noises six or seven years ago in the .Net community. As it happens, the C# language became better for the addition.

Lambda expressions and LINQ in C# and VB.NetLambda expressions were added to C# and VB.Net for Visual Studio 2008, primarily to support LINQ (Language-Integrated Query). Lambda expressions are anonymous functions you can use to create delegates or expression tree types. In C#, to create a lambda expression, you specify input parameters (if any) on the left side of the lambda operator =>, and you put the expression or statement block on the other side. For example, the lambda expression x => x * x specifies a parameter that's named x and returns the value of x squared. In VB.Net, you use anonymous Function or Sub definitions to create lambda expressions.

LINQ is a set of features, also introduced in Visual Studio 2008, that adds query capabilities to the language syntax of C# and Visual Basic. LINQ is useful for querying SQL databases, XML documents, ADO.Net data sets (which may or may not have originated from SQL databases), and .Net collections, files, strings, and so on. The last facility is called LINQ to Objects. The term refers to the use of LINQ queries with any IEnumerable or IEnumerable<T> collection directly. Lambdas are used in method-based LINQ queries as arguments to standard query operator methods such as where.

LINQ and lambda expressions have found wide adoption in the C# community. I expect lambda expressions and their application to Collections to catch on in the Java community in a big way.

Anonymous inner classes in JavaAnonymous inner classes in Java are a baby step toward lambda expressions. You can use them by simply defining the class inline without a name, for example:

JButton testButton = new JButton("Test Button"); testButton.addActionListener(new ActionListener()   {@Override public void actionPerformed(ActionEvent ae){      System.out.println("Click Detected by Anon Class");        }       });

In this example (from an Oracle tutorial) the ActionListener added to the button is defined in an anonymous inner class with an actionPerformed method instead of using a separate named class. This saves a little code, but it's still wordy.

Interfaces that define only a single method, previously called Single Abstract Method interfaces, are called functional interfaces in Java 8. As we'll see, functional interfaces combined with anonymous inner classes are often used with lambda expressions.

Lambda syntax in JavaAs we saw above, the C# lambda operator is =>. The Java lambda operator is ->. (Don't complain to me about the inconsistency. If the syntaxes were the same, anybody could learn them -- then where would we be?)

Like C# lambda expressions, Java 8 lambda expressions tie an argument list to a body. For example, (int x) -> x * x specifies an integer parameter that's named x and returns the value of x squared. As you can see, Java 8 lambda expressions are typed. Fortunately, when the type can be inferred by the context, it can be omitted.

Consider the following three lambda expressions:

(int x, int y) -> x + y 

() -> 42 

(String s) -> { System.out.println(s); }

Now consider the ActionListener example from above, rewritten with a lambda expression:

JButton testButton = new JButton("Test Button"); testButton.addActionListener(e -> System.out.println("Click Detected by Lambda Listener"));

Much better, don't you think? You'll note that because e is the argument of an ActionListener, it comes under the "target typing" clause and its type is inferred.

Standard functional interfacesThe java.util.function package in Java 8 includes five kinds of standard functional interfaces:

  • Predicate: A property of the object passed as argument
  • Consumer: An action to be performed with the object passed as argument
  • Function: Transform a T to a U
  • Supplier: Provide an instance of a T (such as a factory)
  • UnaryOperator: A unary operator from T -> T
  • BinaryOperator: A binary operator from (T, T) -> T

These are just starting points, as you can always define your own interfaces, but they cover the majority of the cases you'll want to use for lambda expressions.

Collections in JavaLambda expressions can help streamline the use of Java Collections, even as they existed before Java 8. In addition, Collections have added features that marry well to lambda expressions, including a new forEach() method defined on every Iterator and Iterable interface.

For example (drawing again on that Oracle tutorial), we can define a List of the Person class, which you can assume has an age member:

List<Person> pl = Person.createShortList();

Then we can define a predicate for selection from the list:

Predicate<Person> allDrivers = p -> p.getAge() >= 16;

Finally, we can do something with our selection from the list:

someClass.doSomething(pl, allDrivers);

If you think about it, that's a lot more compact than the pre-Java 8 ways of accomplishing the same task.

If you want to accomplish a slightly more complicated action, it helps to use the forEach() method mentioned earlier:

pl.forEach( p -> p.printWesternName() );

Suppose you want to apply multiple predicates in series? You can use the new filter() method, which takes a predicate as its argument, and you can chain methods together:

pl.stream().filter(search.getCriteria("allPilots"))   .forEach(Person::printWesternName);

If you think about it, chained filters are inherently more efficient than handwritten loops over collection members. Members that don't meet an early criterion are dropped and never passed to later filters.

You'll notice we used a stream() method at the beginning of our chain -- we needed it to enable chaining. A stream() method takes a Collection as input and returns a java.util.stream.Stream interface as the output. A Stream represents a sequence of elements on which various methods can be chained. Streams can be processed serially or in parallel (using the parallelStream method), which opens up additional possibilities of improving performance. Streams are automatically disposed after use. If you need to save the results, you can copy them into another collection.

We could go on to discuss aggregates and the map method, but I think you have the idea by now.

In summary, lambda expressions in Java 8 offer several improvements to the language. They make many kinds of code easier to read and shorter to write, especially code that can be expressed in functional form. They give the language more expressive power. And they make many kinds of operations more efficient at runtime. Based on the experience of the .Net community with LINQ and lambda expressions, adding functional programming to Java will not weaken its object-orientation in any way. It will make the language richer, stronger, and more elegant.

This article, "Do more with less: Lambda expressions in Java 8," was originally published at InfoWorld.com. Follow the latest developments in Java programming and application development at InfoWorld.com. For the latest business technology news, follow InfoWorld.com on Twitter.

Read more about application development in InfoWorld's Application Development Channel.

Tags application developmentJava Programmingsoftware

More about Oracle

Comments

Comments are now closed

NBN likely to share HFC with Foxtel

READ THIS ARTICLE
DO NOT SHOW THIS BOX AGAIN [ x ]
CIO
ARN
Techworld
CMO