What are Lambda Expressions?

0

Lambda expression is like a method without a name.  Hmm…but isn’t it similar to anonymous inner classes?

Before we get to the details, let’s see an example.

Let’s define a simple function that evaluates x and y, and returns a result.

In mathematical form, we call it f(x, y).

This represents an equation, it doesn’t give any insight into what that equation may be. It can be an addition of two numbers or multiplication or something more complicated.

IFunction:

package com.javarticles.java8.lambda;
public interface IFunction {
    int evaluate(int x, int y);
}

In this article, I will explain you some core concepts about Lambda Expressions with some examples.

Lambda Example

Let’s bring it into use. Below class evaluates the function using an anonymous inner class. It adds two integers and returns the result. You may ask if this is an example of anonymous inner class, then why did I name it LamdaExample. Well…hold your horses…this is still a lambda example 🙂 I will show you next how to achieve the same using lambda expression 🙂

LambdaExample:

package com.javarticles.java8.lambda;

import java.util.function.BinaryOperator;


public class LambdaExample {
    public static void main(String[] args) {
        evaluateFunction(2, 3, new IFunction() {            
            @Override
            public int evaluate(int x, int y) {
                return x + y;
            }
        });          
    }    
    
    public static void evaluateFunction(int x, int y, IFunction f) {
        System.out.println(f.evaluate(x, y));
    }    
}

Here is our first lambda expression.

LambdaExample:

package com.javarticles.java8.lambda;

import java.util.function.BinaryOperator;


public class LambdaExample {
    public static void main(String[] args) {
        evaluateFunction(2, 3, new IFunction() {            
            @Override
            public int evaluate(int x, int y) {
                return x + y;
            }
        });
        evaluateFunction(2, 3, (int a, int b) -> {
                return (a + b);
            }
        );  
    }    
    
    public static void evaluateFunction(int x, int y, IFunction f) {
        System.out.println(f.evaluate(x, y));
    }    
}

Let’s analyse the functional style a bit.

evaluateFunction(2, 3, (a, b) -> a + b);

It has an argument list: (int a, int b). Next there is an arrow symbol and then a body enclosed in a block: return (a+b);.

Simplified Lambda Expression

This still looks a bit verbose so let’s simplify it further.

LambdaExample:

package com.javarticles.java8.lambda;

import java.util.function.BinaryOperator;


public class LambdaExample {
    public static void main(String[] args) {
        evaluateFunction(2, 3, new IFunction() {            
            @Override
            public int evaluate(int x, int y) {
                return x + y;
            }
        });
        evaluateFunction(2, 3, (int a, int b) -> {
                return (a + b);
            }
        );
        evaluateFunction(2, 3, (a, b) -> a + b);      
        evaluateFunction(2, 3, (a, b) -> a * b);
    }    
    
    public static void evaluateFunction(int x, int y, IFunction f) {
        System.out.println(f.evaluate(x, y));
    }    
}

We have simplified the argument list to (a, b) as the compiler manages to infer the argument types from the target type IFunction.

How does Lambda type inference work?

Based on the context in which the lambda expression is passed, the runtime system dynamically creates a subtype of the target type. The compiler takes a look at the context in which the lambda expression is defined and figures out which type is required. In our case the target type is IFunction. There is only one method defined in IFunction and that is int evaluate(int x, int y). Compiler manages to infer type of argument a by mapping it to the first parameter x in evaluate method.

Now this is possible only if the target interface has just one single abstract method. These interfaces containing a single abstract method are called functional interfaces or SAM type where SAM stands for Single Abstract Method. Thus, lambda expressions are only possible if the target interface is a functional interface. In Java, there many existing functional interfaces like Runnable, Comparable, Callable etc.

Let’s see one more example and then we will conclude. Below interface is another functional interface with an abstract method which doesn’t accept any arguments.

ISomeFunction:

package com.javarticles.java8.lambda;

public interface ISomeFunction {
    void doSomething();
}

Using lambda expression, we want to print something in doSomething().

Let’s modify lambda example, call doSomething() and pass ()->System.out.println("Hello! I am lambda without argument")) to it.

Note the beginning of the expression has an empty bracket (). This is because the target interface’s method to which it is mapped, ISomeFunction.doSomething(), doesn’t expect any arguments. Rest of the expression is already known to us.

LambdaExample:

package com.javarticles.java8.lambda;

public class LambdaExample {
    public static void main(String[] args) {
        evaluateFunction(2, 3, new IFunction() {            
            @Override
            public int evaluate(int x, int y) {
                return x + y;
            }
        });
        evaluateFunction(2, 3, (int a, int b) -> {
                return (a + b);
            }
        );
        evaluateFunction(2, 3, (a, b) -> a + b);
        evaluateFunction(2, 3, (a, b) -> a * b);     
        doSomething(()->System.out.println("Hello! I am lambda without argument"));
    }    
    
    public static void evaluateFunction(int x, int y, IFunction f) {
        System.out.println(f.evaluate(x, y));
    }    
    
    public static void doSomething(ISomeFunction someFunction) {
        someFunction.doSomething();
    }
}

Ok what have we learnt from the examples. Let’s summarize.

  1. A lambda definition is a method kind of code without a name.
  2. It lacks a return type, a throws clause, and a name. Why? Main reason is to simplify code so that one can simply pass the function the way we pass data.
  3. Return type and exceptions are inferred by the compiler from the lambda body. If there is a possibility, the compiler can infer even the argument types.
  4. A functional interface is an interface with a single abstract method
  5. If you want to use the lambda expression, the target type must be a functional interface

Finally, before I conclude, there is one important question still unanswered. what do we mean by the name ‘Lambda’? The term lambda is the 11th letter of Greek alphabet (uppercase Λ, lowercase λ), it is also used in Lambda calculus, which is a theoretical framework for describing functions and their evaluation. In programming languages such as Lisp, Python and Ruby, lambda is an operator used to denote anonymous functions or closures, following the usage of lambda calculus. I guess that’s the reason why the feature is called Lambda.

We will revisit lambda at a later point with some more stuff.

Download the source code

This was an example of Java 8 Features Lambda Expressions. You can download the source code here: lambdaExamples.zip

Share.

Comments are closed.