Introduction to lambda expressions

In this article I will give you a quick introduction about one of Java 8’s new features: lambda expressions. I will show you what lambda expressions are and how can you utilize this new feature.

What are lambda expression?

Lambda expressions are something like closures known from other programming languages — or anonymous functions or inner classes known from Java itself.

Lambda expressions want to leverage the usage of anonymous classes. You may remember how it is to register an action listener to a button in a graphical user interface:

JButton clickMeButton = new JButton("Click me!");
clickMeButton.addActionListener(new ActionListener(){

    @Override public void actionPerformed(ActionEvent event){
        System.out.println("You clicked me!");
    }
});

Yes, this code above shows you an anonymous inner class which is only required to implement the only method actionPerformed(ActionEvent) of the ActionListener interface. And this is clumsy — even if modern IDEs can help you with code completion to fill in the body of the method.

However with lambda expressions you can implement this method simply like this:

JButton clickMeButton = new JButton("Click me!");
clickMeButton.addActionListener(event -> System.out.println("You clicked me!"));

This is much more better, isn’t it? Now it is time to take a deeper look at how you can construct your own lambda expression.

Lambda expressions have the following syntax:

parameters -> expression body

The key to lambda expression is the -> arrow symbol.

Let’s look at a basic example:

(int x) -> { return x * x; };

There are some other characteristics which make the usage of lambda expression easier:

  • parentheses are optional around the parameters if only one parameter is required
  • type declaration of the parameters are optional
  • return statement is optional
  • curly braces around the expression body are optional — if the expression body contains a single statement

Because our simple expression qualifies for all four points mentioned above we can rewrite this lambda expression like this:

x -> x * x;

This makes things more readable.

Functional interfaces

Lambda expressions can be used to implement methods of so called functional interfaces. Newer interfaces can have the annotation @FunctionalInterface to indicate that this interface is a functional one — however every interface qualifies as a functional interface which has only one abstract method. That’s why ActionListener’s addActionListener can be replaces / implemented with a lambda expression even if the interface is not informally marked as a FunctionalInterface.

default methods are not counted as abstract because they have an implementation. If the interface overrides methods from java.lang.Object without providing an implementation this does not count as abstract either because the implementation will inherit the implementation from the java.lang.Object class. Let’s see an example:

/**
 * @author GHajba
 *
 */
public interface ExampleFunctionalInterface {

    /**
     * @return a name
     */
    String getName();

    /**
     * prints a greeting
     */
    default void printGreeting() {
        System.out.println("Hello " + getName() + "!");
    }

    /**
     *
     * @return the string representation of this object
     */
    @Override
    String toString();
}

The interface above is a functional interface even if we do not add the annotation. Now let’s create a lambda and use it. Remember, our lambda has to implement only the getName() function which does not need any parameters and returns a String:

final ExampleFunctionalInterface example = () -> "GHajba"; // implements the getName() function
example.printGreeting();
System.out.println(example); // this calls example.toString() implicitly

If we run the example we get a result similar to this:

Hello GHajba!
ChangeCalculator$$Lambda$1/321001045@1c20c684

As you can see, the default method works fine with the lambda implemented and the toString is called from java.lang.Object because a direct implementation in the lambda expression is not present.

Some more examples

After we know lambda expressions, let’s see some examples where we can use them to improve our existing or new code.

Because lambda expressions are anonymous functions or inner classes we can replace calls to existing anonymous functions with them. Just like a GUI application’s action listeners or local implementations of the Runnable interface.

final Runnable runnable = new Runnable() {

    @Override
    public void run() {
        System.out.println("Hello lambdas?");
    }
};

runnable.run();

The example above implements the Runnable interface with an anonymous class. Now for this simple “Hello lambdas?” output this is quite some work to do.

final Runnable runnable_lambdas = () -> System.out.println("Hello lambdas!");
runnable_lambdas.run();

The code example above uses lambda expressions and does exactly the same thing: it prints a string “Hello lambdas!” to the console — however without the fuzz of creating an anonymous class to implement the Runnable interface. Naturally, this code works only with functional interfaces (interfaces, which have only one method).

And as you can see, parameterless lambdas can be created too. For this you need to provide empty parentheses as the parameters to indicate that there is nothing.

Beside this, lambda expressions are closures too. This means, they are functions you can save in a variable and re-use them later. If we take a look at our example from the previous section, we can save it into a variable called square and re-use it later:

IntUnaryOperator square = x -> x * x;

To use it, we can call it either directly or use it in some stream expression, like map:

System.out.println(square.applyAsInt(128));
System.out.println("------");
IntStream.rangeClosed(1, 10).map(square).forEach(System.out::println);

The result of the code snippet above would be this:

16384
------
1
4
9
16
25
36
49
64
81
100

Currying

Currying is the name of partial function application. This means, you have a function which takes more than one parameters but most of the parameters are known already and will stay the same — just one will change. And instead of providing every parameter when you call the function you can curry the function partially and set some default parameters.

Let’s take a look at the following example:

public static Function<Integer, Function<Integer, Integer>> multiply() {
    return x -> y -> x * y;
}

This code creates a simple Function which multiplies two variables of type Integer together. Because the returned function requires two arguments (x and y) we can create curried versions of these functions and use them later:

final Function<Integer, Integer> two_times = multiply().apply(2);
final Function<Integer, Integer> three_times = multiply().apply(3);

Now we have applied (curried) one variable to each of the functions. If we call these functions (apply the second variable) we can see that the results represent the values already applied:

System.out.println(two_times.apply(3)); // prints 6
System.out.println(three_times.apply(3)); // prints 9

Naturally calling apply() every time is a bit overkill but Java cannot make it currently better. It would be a better syntax if you could write following:

final Function<Integer, Integer> two_times = multiply()(2)
System.out.println(two_times(3));

This does not work currently but it would be a nice syntactic sugar to add to the Java language.

Conclusion

Lambda expressions are powerful if you want to implement functional interfaces without the much ado of creating and instantiating a class implementing the interface — mostly because you only need it once (for example as an ActionListener for a button).

We looked at currying / partial function application and we discussed that some features would it make easier to use in Java and hopefully in a future release we can work with it.

By | 2016-09-26T16:40:22+00:00 September 26th, 2016|Core Java|0 Comments

About the Author:

Leave A Comment