Lambda Expressions: Introduction

Lambda Expressions

Lambda expressions were introduced in Java 8 back in 2014. Many years have passed since then and Java programmers have worked with these expressions for a while. Thus, there is enough content and experience in the community to produce not only an introductory tutorial but also some well-established patterns and practices using lambda expressions.

Introduction

Before starting with lambda expressions, we should define what a functional interface is.

Functional Interface

A functional interface is an interface that has only one abstract method.

public interface MyFunctionalInterface {
	String myAbstractMethod(String str);
}

An interface can have multiple methods with default implementations and will still qualify as a functional interface as long as it has only one abstract method.

Creating a simple Lambda expression

Why is a functional interface important for lambda expressions though?

As we will see next lambda expressions can only be used on the abstract methods of a functional interface. It’s a condition that cannot be avoided.

We can use the abstract method that we declared in the above example with a lambda expression:

MyFunctionalInterface sayHello = (name) -> {return "Hello " + name;};

Let’s analyze the above lambda. It’s divided into two parts:

  • The part before the “->” is the parameters for the abstract method we are trying to use. In this case, myAbstractMethod expects a single argument of type String. It can also be written (String name) but the parameter type can be omitted. The compiler will try to map the parameter to the only abstract method in MyFunctionalInterface.
  • The part after “->” is our implementation of the method. It can have multiline code as any method and a return statement as well.

Lambdas and Anonymous Interface Implementations

At this point, lambda expressions are very similar to anonymous interface implementations. However, they have some significant differences:

  • Lambdas can only be used on Functional Interfaces, whereas anonymous interface implementations have no such restriction.
  • Anonymous interface implementations behave like a newly defined class. They have their own scope (fields) and then compiled a new file containing the anonymous interface implementation will be created as it would with any other class or interface. Fields cannot be defined inside a lambda body, but fields defined outside can still be accessed. Lambdas that access fields outside of the class are often called capturing lambdas.

We can see the use of scope in both cases:

String name = "Jim";

MyFunctionalInterface anonymousImplementaion = new MyFunctionalInterface() {
			String name = "George";
			@Override
			public String myAbstractMethod(String str) {
				return "Anonymous implementation: " + str + this.name;
	}
};

MyFunctionalInterface lambda = (n) -> {
	return "Lambda: " + n + name;
};

System.out.println(anonymousImplementaion.myAbstractMethod("Hello "));
System.out.println(lambda.myAbstractMethod("Hello "));

Using Lambda expressions

Continuing with our example, let’s see how we can use it in code:

MyFunctionalInterface sayHello = (name) -> {return "Hello " + name;};
MyFunctionalInterface sayGoodbye = (name) -> {return "Goodbye " + name;};

System.out.println(sayHello.myAbstractMethod("Jim"));
System.out.println(sayGoodbye.myAbstractMethod("Jim"));

Output:

Hello Jim
Goodbye Jim

Advanced usage

One parameter

If our lambda has only one parameter we can omit the parenthesis.

MyFunctionalInterface sayHello = name -> {return "Hello " + name;};

Multiple Parameters

public interface Calculator {
	int calculate(int number1, int number2);
}


Calculator sum = ((x, y) -> x + y);
Calculator mult = ((x, y) -> x * y);
Calculator max = ((x, y) -> x > y ? x : y);

System.out.println(sum.calculate(3,6));
System.out.println(mult.calculate(3,6));
System.out.println(max.calculate(3,6));

Output:

9
18
6

No parameter

public interface MyFunctionalInterface {
	String myAbstractMethod();
}

MyFunctionalInterface sayHelloWorld = () -> {return "Hello world";};

Return statement

When our lambda expression has just a return statement the “return” can be omitted along with the brackets.

So, we can go from this:

MyFunctionalInterface capitalize = str -> {return str.toUpperCase();};

To this:

MyFunctionalInterface capitalize = str -> str.toUpperCase();

Much cleaner. But we can do even better:

MyFunctionalInterface capitalize = String::toUpperCase;

Our lambda only passes a string parameter and calls toUpperCase method. In this case, we can call the method by using “::” and the compiler will know that this is a method reference. The object or class that owns the method goes before the “::” followed by the method name. We can reference both static or instance methods, as well as constructors.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.