Java Lambda Expression

[Updated: Feb 14, 2016, Created: Nov 22, 2015]

What are Lambda Expressions?

Basically Lambda expressions are new alternative to anonymous classes with an obvious benefit of avoiding boilerplate code. But there is more to that. They are different in semantics as well. We will first get familiar with the new syntax then will go into concepts in next tutorials.


Lambda Expression works with Interfaces having only one abstract method. Aren't all methods in an interface abstract? Not anymore, after introduction of default methods and static methods in Java 8. We will cover that in another tutorial.

The interface does not have to be user defined. It can be any interface, defined in previous versions of JDK or of any third party library. It has to have only one method, does not matter whatever the signature is or how many arguments it has or whether it returns a value or not.



How to use Lambda Expressions?

Let's take java.lang.Runnable interface for our example. Put this code in our main method.

 Runnable runnable = new Runnable(){
    @Override
    public void run() {
        System.out.println("running");
    }
};

Let's focus on right side of = operator and recognize the parts which can be categorized as boilerplate code

  1. The Runnable initiation with new operator i.e. new Runnable(): Well it's obvious from the left side declaration part that we want to initiate a Runnable with new operator unless we what to assign a sub type to super type. In that case lambda expressions don't work so rule out that scenario here. Now we recognized this part is unnecessary code and compiler can infer that itself. That's why Java lambda expression designers got rid of that part. Now we are left with this (please don't try to compile it as we still have to get rid more parts to make it a valid expression)
     Runnable runnable = {
        @Override
        public void run() {
            System.out.println("running");
        }
    };
  2. Overriding public void run() method: This is also unnecessary part. It's understood that when we are initializing an interface anonymously then we have to override the methods that interface has. There's no escaping. So let's get ride of that as well, the compiler can infer that part too. But wait, how about in case the method has some parameter list. Will that be unncesary too? Not at all. The code inside method body needs them to do what it's supposed to do. So let's leave the parameter parenthesis (), but get ride of the rest of method declaration.
    Runnable runnable = {
                       () {
            System.out.println("running");
        }
    };
  3. The method body: This part is absolutely necessary. It's the essence what we want to achieve by running that method. Also we have seen the method parameters are also needed inside the method body. Two parts: the body and the parameter list are the essence. In our example we don't have parameters but we still have to tell that to the compiler by providing empty parenthesis ().
  4. The curly braces : The outer braces are not needed. The compiler will think that you are going to initialize array. So we have to get ride of that. The body's left over braces are optional if there's only one line in it, so we are going to get ride of that too. In case we have multiple lines then we have to keep that braces. Now we got this:
    Runnable runnable = () System.out.println("running");
    

We have simplified our code by removing all boilerplate code, but it's still not a valid lambda expression yet. we have to give some suitable hint to the compiler that we are declaring lambda expression The designers decided to use the operator -> for that purpose. This operator is placed between method parameter parenthesis and the body. Just remember this phrase: parameters to body


Finally we got a valid lambda expression:

Runnable runnable = () -> System.out.println("running");

If we execute runnable.run() we will see the output "running"

It's quite obvious that this kind of code going to increase developer productivity and code readability.


The general syntax of a lambda expression:

 InterfaceWithOneMethod interfaceInstance = (Parameter List) -> {Method Body};
where:
  1. InterfaceWithOneMethod: should be any interface having only one abstract method.
  2. Parameter List: Comma separated parameter list. We can optionally omit parameter type here. For example consider
    (String str) -> {....};
    We can just write that as
    (str) -> {....};
    Without parameter types, it looks like dynamic typing but it's same static typing that Java always had. If the compiler cannot infer the type, we will have compile time error.
  3. Method Body: should return a value as usual if the target method of the InterfaceWithOneMethod returns a value.

In simple words right side represents the method of the interface in a short form

In above example runnable can be passed around and can be invoked multiple times. So what? We could also do the same thing with anonymous instances. Well, there's a fundamental difference. Please read next


More Examples:

In following examples we will replace the anonymous classes with lambda expressions

Anonymous Class Lambda Expression
List<Integer> list = Arrays.asList(2, 1, 4, 3);
Collections.sort(list, new Comparator<Integer>() {
    @Override
    public int compare(Integer a, Integer b) {
        return a.compareTo(b);
    }
});
Replacing java.lang.Comparator:
List<Integer> list = Arrays.asList(2, 1, 4, 3);
Collections.sort(list, (a, b) -> a.compareTo(b));
File[] textFiles = new File("D:\\myFolder")
        .listFiles(new FileFilter() {
            @Override
            public boolean accept(File f) {
                return f.getName().endsWith(".txt");
            }
        });
Replacing java.io.FileFilter
File[] textFiles = new File("D:\\myFolder")
    .listFiles(f -> f.getName().endsWith(".txt"));
ExecutorService executorService =
        Executors.newSingleThreadExecutor();
Future<String> future =
        executorService.submit(new Callable<String>() {
            @Override
            public String call() throws Exception {
                return "done";
            }
        });
Replacing java.util.concurrent.Callable<V>
ExecutorService executorService =
        Executors.newSingleThreadExecutor();
Future<String> future =
        executorService.submit(() -> "done");

Note: It is a compile-time error if a lambda expression occurs in a program in someplace other than an assignment context. That means lambda expression must be assigned to a compatible interface or pass to a method as argument. That makes sense because how compiler will infer the augments and return type of an lambda expression if there's no assignment.


How Lambda Expression can change programming style?

Lambda expressions will change the Java programming style only at the situations where logic is merely functional rather than object oriented. For example asynchronous computation call back, collection transformations etc are functional. Now we don't have to inline anonymous classes to customzie some functional behavior, we just have to provide a lambda expression in a productive manner. We will be writing less code, there's going to be less util methods, less meta data(e.g. annotation) etc. The oops is not effected in any ways. Simply this kind of constructs can remove the boilerplate code only at the "functional" places.

See Also