spk-logo-white-text-short
0%
1-888-310-4540 (main) / 1-888-707-6150 (support) info@spkaa.com
Select Page

Exploring Java 8’s New Feature: Lambda Expressions

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 Hybrid-Remote Playbook

The Hybrid-Remote Playbook

Post-pandemic, many companies have shifted to a hybrid or fully remote work environment. Despite many companies having fully remote workers, many still rely on synchronous communication. Loom offers a way for employees to work on their own time, without as many...

Related Resources

Optimize Your Databases with Azure SQL

Optimize Your Databases with Azure SQL

Making data-driven decisions is one of the most valuable things a business can do to achieve and maintain success. Businesses thrive on their ability to make intelligent, timely decisions based on accurate, accessible data. Without the use of data to inform their...

How Model-Based Definition (MBD) Cuts ECOs by 41% and Scrap by 47%

How Model-Based Definition (MBD) Cuts ECOs by 41% and Scrap by 47%

Organizations are increasingly turning to Model-Based Definition (MBD) to revolutionize their engineering and manufacturing processes. By embedding rich, digital annotations directly into 3D models, MBD provides a single source of truth for product definitions. This...

Seamlessly Transition from AWS CodeCommit to GitLab

Seamlessly Transition from AWS CodeCommit to GitLab

In July of 2024, AWS announced that AWS CodeCommit would no longer be sold to new customers.  And thus begins the journey of winding down a product for AWS.  As AWS CodeCommit approaches its end-of-life, many organizations face a tough decision. Choosing where to...