fbpx
1-888-310-4540 (main) / 1-888-707-6150 (support) info@spkaa.com
Select Page

Exploring Java 8’s New Feature: Lambda Expressions

windchill features best plm software
Written by SPK Blog Post
Published on May 12, 2014

Recently, Oracle announced the release of Java 8. This latest edition has some interesting new features, most notably the inclusion of lambda expressions and the ability to do multiple inheritance through default methods.

We’ll spend the bulk of this article exploring lambda expressions and how they can be useful for engineering — but first, let’s take a quick overview of what’s been added in this release of the language and some notes about getting started with it.

Java 8’s Distinctly New Features:

  • Default methods in Interfaces (which make multiple inheritance possible)
  • Lambda expressions
  • A JavaScript runtime which allows developers to embed JavaScript within applications
  • Annotations on Java Types
  • Unsigned integer arithmetic
  • Repeating annotations
  • Date and Time API
  • Statically linked JNI libraries
  • Launch JavaFX applications

Notes On Getting Started

There are a couple of things to be aware of when downloading Java 8. The first of which is Java 8’s incompatibility with Windows XP. That said, there is a way around this if using Java 8 on Windows XP is necessary. You can do a forced installation by directly unzipping from the installation executable

A second piece of information that may be helpful is at the time of this writing, the Eclipse IDE does not support Java 8 out of the box — you will need to install a feature patch to make this happen. To do so, navigate to Help > Install New Software… and enter “http://download.eclipse.org/eclipse/updates/4.3-P-builds/” into the “Work with” field. Select category “Eclipse Java 8 Support (for Kepler SR2)” and complete the download/install process.

The newest version of Eclipse (Luna) is scheduled for release on June 25th, at which point Java 8 will be supported without any special updates.

New Feature: Lambda Expressions

In essence, a lambda expression is an anonymous method — a method that can be defined without being bound to an identifier.

Lambda expressions have been around since the late 50’s in LISP and have been incorporated into mainstream languages such as JavaScript, Perl, Python, Ruby, and C#, but until this release they were not possible in Java.

According to Oracle, the reasons for their inclusion in Java are that “[Lambda expressions] provide a clear and concise way to represent [a] one method interface using an expression. Lambda expressions also improve the Collection libraries making it easier to iterate through, filter, and extract data from a Collection”. These one method interfaces are also officially known as “Functional Interfaces”. For example, the ActionListenter interface is a Functional Interface because it defines the actionPerformed method and no others.

In the previous versions of Java, using functional interfaces with anonymous inner classes was a common pattern. Despite getting the job done, there are some downsides to using them this way. In particular, there is what’s known as “The Vertical Problem”. Writing an anonymous inner class typically results in an awkward block of code that spans multiple lines in order to accomplish something that is conceptually very straight-forward. When all we want to say is “Execute this method when I click this button” We end up writing “When the user clicks this button, create an instance of class X which specifies the Y method and inside that Y method I want method Z to execute”.

The following code will illustrate this point:

/* Old style, using anonymous inner classes */

JButton button = new JButton("Click Me!");

button.addActionListener( new ActionListener() {

        @Override

               public void actionPerformed( ActionEvent e ) {

                               printButtonLabel( button.getText() );

               }

} );

This isn’t horrible, but imagine creating 20 buttons and assigning them various kinds of behaviors. You can probably imagine the code would grow very large and would become more challenging to follow.

Now see how this could be done with a lambda expression.

/* New style, using lambda expressions */

JButton button = new JButton("Click Me!");

button.addActionListener( e -> printButtonLabel( button.getText() ) );

This is much more concise. It saves several lines of code and makes the behavior of the button very clear. This works because ActionListener is a functional interface and defines only one method. Java knows that when we call the addActionListener method of the JButton class, that there can only be one type of object passed in and that object has only one method that can possibly be called, so it essentially allows us to use a shorthand. In the case above, the parameter e is the ActionEvent object passed to the actionPerformed method and the content to the right of the arrow token is the body of the actionPerformed method. Our example executes only one method call, however, an arbitrarily long block of code can be inserted if placed between curly braces “{ …. }”.

Syntax of A Lambda Expression

The syntax of a lambda expression is composed of three parts

        Argument List                    Arrow Token                     Body

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

The argument list contains the parameters required by the method define inside the functional interface. The parentheses themselves are optional if the method has one or more parameters and object type is optional as well. As a result, the following are all valid expressions, given that they conform to the interfaces:

() -> System.out.println(“No paramerts”);

(String x) -> System.out.println(“One parameter” + x);

(x, y) -> System.out.println(“Two parameters” + x + “ and “ + y);

x -> System.out.println(“Parentheses are optional”);

The arrow token separates the argument list from the code body.

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 and a return statement returns control to the caller of the anonymous method. The break and continue keywords are illegal at the top level, but are permitted within loops. If the body produces a result, every control path must return something or throw an exception.

Java 8 provides a new annotation to label an interface as a functional interface.

Just precede the interface declaration statement with “@FunctionalInterface”.  This isn’t required, but provides some extra information to the compiler to help keep things consistent by enforcing the rule that the interface only define one method. This new annotation is similar to the @Override annotation in earlier versions of Java.

Using Lambdas to Write Dispatch Tables

In addition to reducing the amount of typing necessary, lambda expressions make it easy to code things like dispatch tables for processing things like keyboard input (such as you might need to do if you’re a creating a game).

In my dabbling with game programming, almost every example I’ve seen of processing keyboard input usually involves a long string of if statements that attempts to handle all the valid key inputs. I’ve never liked the way this looks, but I didn’t know what to do about it. The following example, I hope, shows a much simpler way to process something of this nature.

import java.awt.BorderLayout;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.util.HashMap;
import javax.swing.JFrame;
import javax.swing.JTextField;

public class Test {

    @FunctionalInterface
    interface KeyAction {
        public void doAction();
    }

    public static void main( String[] args) {

        HashMap<Integer, KeyAction> keyDispatcher = new HashMap<Integer, KeyAction>();

        keyDispatcher.put(KeyEvent.VK_W, () -> moveUp());
        keyDispatcher.put(KeyEvent.VK_S, () -> moveDown());
        keyDispatcher.put(KeyEvent.VK_A, () -> moveLeft());
        keyDispatcher.put(KeyEvent.VK_D, () -> moveRight()); 

        // Using a JTextField out of simplicity
        JTextField field = new JTextField();
        field.addKeyListener(new KeyAdapter() {
            @Override
            public void keyPressed(KeyEvent arg0) {
                try{

                    keyDispatcher.get(arg0.getKeyCode()).doAction();

                } catch (NullPointerException e) {
                    System.out.println("That button doesn't do anything yet...");
                }
            }
        });

        JFrame frame = new JFrame("Listener Frame");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        frame.add(field, BorderLayout.NORTH);
        frame.pack();
        frame.setVisible(true);
    }

    private static void moveUp() {
        System.out.println("Moving up");
    }
    private static void moveDown() {
        System.out.println("Moving down");
    }
    private static void moveLeft() {
        System.out.println("Moving left");
    }
    private static void moveRight() {
        System.out.println("Moving right");
    }
}

The thing to note about this example is how clearly the input value is associated with the desired action and how efficiently the desired action is performed due to the constant lookup time of the hash table.

To do the above in prior versions of Java would require some clunky anonymous inner classes or some questionable use of Java’s reflection library. See this StackOverflow article for an example.

Hopefully, this article has provided a decent introduction to one of the more exciting additions to the Java language. If you want to pursue the subject further, I recommend the following resources:

Next Steps:

David Hubbell
Software Engineer
SPK and Associates

Latest White Papers

The Next Chapter of Jira Service Management

The Next Chapter of Jira Service Management

The service industry is only becoming more competitive as the years pass, making efficient delivery vital to success. Development and Operations teams need to work together to deliver aid and Jira Service Management can help achieve this. Explore the future of Jira...

Related Resources

Automatically Visualizing Dependencies in Codebeamer

Automatically Visualizing Dependencies in Codebeamer

If you work in the software and systems engineering space, you likely understand that managing dependencies across multiple components and requirements is critical for project success. Unfortunately, specifications can be difficult to track, and dependencies hard to...

Exploring Modern Software Deployment Strategies

Exploring Modern Software Deployment Strategies

Deploying software can feel like a gamble due to all the strategies and solutions on the market, but it doesn’t have to be. Discovering which software deployment strategy works best for your organization is a great place to start. This strategy, combined with a modern...