Java 11 Developer Certification - Lambda Expressions Code Examples

April 14, 2021

What we are covering in this lesson

  1. Single Parameter Lambda Expressions
  2. Multiple Parameter Lambda Expressions
  3. Java provided Functional Interfaces
  4. ArrayList with Lambda Expressions

Single Parameter Lambda Expressions

In this section, we are going to demonstrate a single parameter lambda expression example. We will do it with the below class called Calculator.

So we have a single class Calculator, containing a functional interface UnaryIntegerOperation. Just to reiterate, a functional interface is an interface with one and only one abstract method.

Below we have a calculate method which accepts an int and an instance of UnaryIntegerOperation.

In the main method, we have created multiple lambda expressions in there.

In the first expression square, we have specified type, so mandatory to specify parenthesis as well. cube uses single untyped parameter in lambda expression. increment also specifies a single untyped parameter in lambda expression without paranthesis, and it is perfectly valid since we have not specified the type.

So how does this work? Basically the compiler took the right hand side of the lambda expression and built a concrete method designed using the interface method Calculate and then take in parenthesis, then populate it with code to right of the arrow token. The name of the method is actually unnecessary to the compiler, but you should note that it is the interface’s method that is the template for the on-the-fly method creation and execution. We should also note that the interface’s method returns an int, but we didnt need to specify return in the first 3 lambda expressions.

It’s actually unnecessary and invalid to use the word return in a single statement expression. The compiler will do the extra work to properly return a value if the defined method requires it.

Now, in the last expression, we’re using curly braces to wrap the expression body. So there’s two things specifically to note here. First, here we’re using a return statement. And secondly, statements in the left and right curly braces but the block requires semicolons. The single statement expressions did not actually require them. But actually in talking about those first three lambda expressions, the declaration itself required the semicolon at the end of the line but not actually the statements themselves.

package maxCode.online.lambdaExpressions;

//Calculator Class
public class Calculator {
	// We declare a functional interface as part of the class.
	// A functional interface is an interface with one and only one
	// abstract method.
	interface UnaryIntegerOperation {
		int calculate(int a);
	}

	// We create a 'pass thru' method, accepting an object which
	// implements our interface as one parameter. The other parameter
	// is the number we'll be doing the operation on.
	public int calculate(int a, UnaryIntegerOperation op) {
		return op.calculate(a);
	}

	public static void main(String... args) {
		// We create an instance of our class
		Calculator myApp = new Calculator();

		// This lambda expression demonstrates a single typed parameter in
		// parentheses (if you specify type, you MUST use parentheses)
		// -- This operation will result in the value being squared.
		UnaryIntegerOperation square = (int a) -> a * a;

		// This lambda expression demonstrates a single untyped parameter in
		// parentheses
		// -- This operation will result in the value being cubed.
		UnaryIntegerOperation cube = (a) -> a * a * a;

		// This lambda expression demonstrates a single untyped parameter
		// without using parentheses
		// -- This operation will result in the value being incremented by 1.
		UnaryIntegerOperation increment = a -> a + 1;

		// This lambda expression demonstrates a single untyped parameter
		// without using parentheses, but our body is wrapped in brackets
		// and we use a return statement
		// -- This operation will result in the value being decremented by 1.
		UnaryIntegerOperation decrement = a -> {
			return a - 1;
		};

		// Execution
		int value = 2;
		System.out.println("The number (" + value + ") squared = " + myApp.calculate(value, square));
		System.out.println("The number (" + value + ") cubed = " + myApp.calculate(value, cube));
		System.out.println("The number (" + value + ") incremented = " + myApp.calculate(value, increment));
		System.out.println("The number (" + value + ") decremented = " + myApp.calculate(value, decrement));
	}
}

Output

The number (2) squared = 4
The number (2) cubed = 8
The number (2) incremented = 3
The number (2) decremented = 1

If we try to remove the word return in the last expression, there’s something called an error indicating that a - 1 is not a statement. When you use the left and right curly braces you’re required to use the return statement if your functional interface method returns a value.

Now lets look at multiple parameters for Lambda Expressions

Multiple Parameter Lambda Expressions

Here we will use a similar class as previous example, except that the interface method accepts two parameters, and the lambda expressions have got two parameters.

Parenthesis are always required in case of multiple parameters (for typed as well as non-typed) in lambda expressions. Some invalid examples are commented out so that the code compiles properly.

Note that in the multiplication example, we’re using the local variable type inference by setting type to var. Support for var in the parameters of Lambda Expressions was added in Java 11.

package maxCode.online.lambdaExpressions;

public class BinaryCalculator {
	// We declare a functional interface as part of the class
	// A functional interface is an interface with one and only one
	// abstract method.
	interface BinaryIntegerOperation {
		int calculate(int a, int b);
	}

	// We create a 'pass thru' method, accepting an object which
	// implements our interface as one parameter. The other parameters
	// are the numbers used in the binary operations
	public int calculate(int a, int b, BinaryIntegerOperation op) {
		return op.calculate(a, b);
	}

	public static void main(String... args) {
		BinaryCalculator myApp = new BinaryCalculator();

		// This lambda expression demonstrates a typed parameter list
		// Parentheses are always required for multiple parameters
		// - This operation will result in the values being added together.
		BinaryIntegerOperation addition = (int a, int b) -> a + b;

		// This lambda expression demonstrates an untyped parameter list
		// Parentheses are always required for multiple parameters
		// - This operation will result in the values being subtracted
		BinaryIntegerOperation subtraction = (a, b) -> a - b;

		// BinaryIntegerOperation multiplication = (int a, b) -> a * b;
		//
		// BinaryIntegerOperation division = a, b -> a / b;

		BinaryIntegerOperation multiplication = (var a, var b) -> a * b;

		BinaryIntegerOperation division = (a, b) -> a / b;

		// Execution
		int value_a = 5;
		int value_b = 3;

		System.out.println("The numbers (" + value_a + ", " + value_b + ") added = "
				+ myApp.calculate(value_a, value_b, addition));
		System.out.println("The numbers (" + value_a + ", " + value_b + ") subtracted = "
				+ myApp.calculate(value_a, value_b, subtraction));
		System.out.println("The numbers (" + value_a + ", " + value_b + ") multiplied = "
				+ myApp.calculate(value_a, value_b, multiplication));
		System.out.println("The numbers (" + value_a + ", " + value_b + ") divided = "
				+ myApp.calculate(value_a, value_b, division));
	}
}

Output

The numbers (5, 3) added = 8
The numbers (5, 3) subtracted = 2
The numbers (5, 3) multiplied = 15
The numbers (5, 3) divided = 1

Java provided Functional Interfaces

We have stated multiple times now that Lambda Expressions require functional interfaces to provide a targeted method and type. Java provides several interfaces for you that satisfy common patterns, and several of these are in the java.util.function package.

Functional Interface Generic Functional Interface Typed Description
UnaryOperator DoubleUnaryOperator
IntUnaryOperator
LongUnaryOperator
Represents an operation on a single operand that produces a result of the same type as its operand
BinaryOperator DoubleBinaryOperator
IntBinaryOperator
LongBinaryOperator
Represents an operation on two operands of the same type, produces a result of the same type as the operands
Consumer DoubleConsumer
IntConsumer
LongConsumer
Represents an operation that accepts a single input argument and returns no result
Predicate DoublePredicate
IntPredicate
LongPredicate
Represents a predicate (boolean-valued function) of one argument
Supplier DoubleSupplier
IntSupplier
LongSupplier
Represents a supplier of results
Function<T,R> DoubleFunction<R>
IntFunction<R>
LongFunction<R>
Represents a function that accepts one argument and produces a result

Basically, you can see that the interfaces provided by Java really cover a wide swath of functionality. Lets use one of these interfaces in our code.

So we have removed our functional interface in the below code and used the java supplied IntUnaryOperator. Output is the same as before. Just note the extra import statement in the code.

package maxCode.online.lambdaExpressions;

import java.util.function.IntUnaryOperator;

//Calculator Class
public class CalculatorFI {
	// We create a 'pass thru' method, accepting an object which
	// implements our interface as one parameter. The other parameter
	// is the number we'll be doing the operation on.
	public int calculate(int a, IntUnaryOperator op) {
		return op.applyAsInt(a);
	}

	public static void main(String... args) {
		// We create an instance of our class
		CalculatorFI myApp = new CalculatorFI();

		// We create three objects using lambda expressions

		// This lambda expression demonstrates a single typed parameter in
		// parentheses (if you specify type, you MUST use parentheses)
		// -- This operation will result in the value being squared.
		IntUnaryOperator square = (int a) -> a * a;

		// This lambda expression demonstrates a single untyped parameter in
		// parentheses
		// -- This operation will result in the value being cubed.
		IntUnaryOperator cube = (a) -> a * a * a;

		// This lambda expression demonstrates a single untyped parameter
		// without using parentheses
		// -- This operation will result in the value being incremented by 1.
		IntUnaryOperator increment = a -> a + 1;

		// This lambda expression demonstrates a single untyped parameter
		// without using parentheses, but our body is wrapped in brackets
		// and we use a return statement
		// -- This operation will result in the value being decremented by 1.
		IntUnaryOperator decrement = a -> a - 1;

		// Execution
		int value = 2;
		System.out.println("The number (" + value + ") squared = " + myApp.calculate(value, square));
		System.out.println("The number (" + value + ") cubed = " + myApp.calculate(value, cube));
		System.out.println("The number (" + value + ") incremented = " + myApp.calculate(value, increment));
		System.out.println("The number (" + value + ") decremented = " + myApp.calculate(value, decrement));
	}
}

Output

The number (2) squared = 4
The number (2) cubed = 8
The number (2) incremented = 3
The number (2) decremented = 1

The below code has an example of four of the interfaces in the Java.util.function package. And we’re using the generic interface of each and passing a string type for each of them.

The first example was a consumer example. This method returns no result, it just does something on the parameter passed. Here we’re setting up a code block in the Lambda Expression that creates a StringBuilder that takes on the date, time, and the project class information to a string.

Next we’ve got a predicate example there. That method returns a boolean and accepts one parameter and we’re using the predicate in the ArrayList removeIf method to remove any string elements that starts with an ‘A’. We are using the dictionary string array that we defined in the beginning of the code.

Next one is the supplier example. This method returns an object that accepts no parameter. It was simply just returning a string to show how that’s being used.

And the fourth example is a function example The method returns a result and accepts one parameter. So here we’re setting up a code block in the Lambda Expression that uses the repeat method on a past string.

package maxCode.online.lambdaExpressions;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;

public class SuppliedInterfaceTests {

	public static void main(String[] args) {
		// Set up some test data
		String[] dictionary = { "Angry", "Apple", "Art", "Ball", "Box", "Bump", "Cap", "Car", "Cone", "Dart", "Dog",
				"Duck" };

		// -- Consumer Example
		// Method returns no result, just does something on
		// the parameter passed
		Consumer<String> str = s -> {
			StringBuilder sb = new StringBuilder(s);
			sb.insert(0, "MyApplication: SuppliedInterfaceTests : ");
			sb.insert(0, LocalDateTime.now().toLocalTime() + ": ");
			System.out.println(sb);
		};
		str.accept("I want to log this statement");

		// -- Predicate Example
		// Method returns a boolean, accepts one parameter
		Predicate<String> aWords = p -> p.indexOf("A") == 0;
		ArrayList<String> a = new ArrayList(Arrays.asList(dictionary));
		// demonstrate with ArrayList.removeIf method which accepts a
		// Predicate as a parameter
		a.removeIf(aWords);
		System.out.println(a);

		// Now, we demonstrate test method on Predicate
		String apple = "Apple";
		if (aWords.test(apple)) {
			System.out.println(apple + " starts with an A");
		}

		// -- Supplier Example
		// Method returns an object, accepts no parameter
		Supplier<String> stringSupplier = () -> new String("returning a new String Object");
		System.out.println("stringSupplier.get() = " + stringSupplier.get());

		// -- Function Example
		// Method returns a result, and accepts one parameter
		Function<String, String> funkyFunction = (s) -> {
			s = s.repeat(5);
			return s;
		};

		System.out.println("funkyFunction.apply() = " + funkyFunction.apply("oh no "));
	}
}

Output

14:53:24.009: MyApplication: SuppliedInterfaceTests : I want to log this statement
[Ball, Box, Bump, Cap, Car, Cone, Dart, Dog, Duck]
Apple starts with an A
stringSupplier.get() = returning a new String Object
funkyFunction.apply() = oh no oh no oh no oh no oh no

ArrayList with Lambda Expressions

So now we will put lists of Lambda Expressions together to manipulate sets of data.

The class ForEachExamples has a method setValue to create a unique value for an array element in the format Atest1.

Next we’ve got our main method. And here we’re gonna create an array and we’re using the array setAll method to populate data and the setAll method uses a method reference Lambda Expression there to call the method we’ve created the one with two colons.

Then we are using the stream method.

Next we are demonstrating some sorting there by the suffix number descending using a comparable interface So sorting by the numeric suffix of the value in the array descending and then another example there sorting in reverse order (which is basically equivalent to comparator.reverseOrder). We’re using the sort method there with the Lambda Expression.

package maxCode.online.lambdaExpressions;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.function.Predicate;

public class ForEachExamples {
	// We create a method that will be used to set values
	// in a String array. The parameter is the array index.
	// A_TEST_1, B_TEST_2 etc.
	public static String setValue(int indx) {
		int intA = 65;
		int multiplier = indx / 26;
		int cntr = indx - (multiplier * 26);
		return (char) (cntr + intA) + "_TEST_" + (indx + 1);
	}

	// Main method creates array, calls setValue method with
	// Arrays.setAll method
	public static void main(String[] args) {
		// Initialize a String array
		String[] stringArray = new String[50];

		// We use a lambda expression method reference to set the data
		// in the array.
		// The method reference replaced an expression that would have been:
		// (s) -> setValue(s) was replaced with ForEachExamples::setValue
		Arrays.<String>setAll(stringArray, ForEachExamples::setValue);

		// Make an ArrayList out of the array
		ArrayList<String> alist = new ArrayList<String>(Arrays.asList(stringArray));
		System.out.println(alist + "\n");

		// We'll use the stream() method to manipulate the list
		alist.stream()
				.filter( // Only want values that start with A or B
						(Predicate<String>) (s) -> s.startsWith("A") || s.startsWith("B"))

				// method reference used in place of a lambda expression that
				// would look like (s) -> System.out.println(s);
				.forEach(System.out::println);

		// Let's sort by the suffix number descending, using a Comparable
		// interface
		alist.sort((a, b) -> {
			return Integer.valueOf(b.split("_")[2]).compareTo(Integer.valueOf(a.split("_")[2]));
		});
		System.out.println("\n" + alist);

		// Let's sort reverse order
		// Code below is same as:
		// alist.sort(Comparator.reverseOrder());
		alist.sort((a, b) -> b.compareTo(a));
		System.out.println("\n" + alist);
	}
}

Output

[A_TEST_1, B_TEST_2, C_TEST_3, D_TEST_4, E_TEST_5, F_TEST_6, G_TEST_7, H_TEST_8, I_TEST_9, J_TEST_10, K_TEST_11, L_TEST_12, M_TEST_13, N_TEST_14, O_TEST_15, P_TEST_16, Q_TEST_17, R_TEST_18, S_TEST_19, T_TEST_20, U_TEST_21, V_TEST_22, W_TEST_23, X_TEST_24, Y_TEST_25, Z_TEST_26, A_TEST_27, B_TEST_28, C_TEST_29, D_TEST_30, E_TEST_31, F_TEST_32, G_TEST_33, H_TEST_34, I_TEST_35, J_TEST_36, K_TEST_37, L_TEST_38, M_TEST_39, N_TEST_40, O_TEST_41, P_TEST_42, Q_TEST_43, R_TEST_44, S_TEST_45, T_TEST_46, U_TEST_47, V_TEST_48, W_TEST_49, X_TEST_50]

A_TEST_1
B_TEST_2
A_TEST_27
B_TEST_28

[X_TEST_50, W_TEST_49, V_TEST_48, U_TEST_47, T_TEST_46, S_TEST_45, R_TEST_44, Q_TEST_43, P_TEST_42, O_TEST_41, N_TEST_40, M_TEST_39, L_TEST_38, K_TEST_37, J_TEST_36, I_TEST_35, H_TEST_34, G_TEST_33, F_TEST_32, E_TEST_31, D_TEST_30, C_TEST_29, B_TEST_28, A_TEST_27, Z_TEST_26, Y_TEST_25, X_TEST_24, W_TEST_23, V_TEST_22, U_TEST_21, T_TEST_20, S_TEST_19, R_TEST_18, Q_TEST_17, P_TEST_16, O_TEST_15, N_TEST_14, M_TEST_13, L_TEST_12, K_TEST_11, J_TEST_10, I_TEST_9, H_TEST_8, G_TEST_7, F_TEST_6, E_TEST_5, D_TEST_4, C_TEST_3, B_TEST_2, A_TEST_1]

[Z_TEST_26, Y_TEST_25, X_TEST_50, X_TEST_24, W_TEST_49, W_TEST_23, V_TEST_48, V_TEST_22, U_TEST_47, U_TEST_21, T_TEST_46, T_TEST_20, S_TEST_45, S_TEST_19, R_TEST_44, R_TEST_18, Q_TEST_43, Q_TEST_17, P_TEST_42, P_TEST_16, O_TEST_41, O_TEST_15, N_TEST_40, N_TEST_14, M_TEST_39, M_TEST_13, L_TEST_38, L_TEST_12, K_TEST_37, K_TEST_11, J_TEST_36, J_TEST_10, I_TEST_9, I_TEST_35, H_TEST_8, H_TEST_34, G_TEST_7, G_TEST_33, F_TEST_6, F_TEST_32, E_TEST_5, E_TEST_31, D_TEST_4, D_TEST_30, C_TEST_3, C_TEST_29, B_TEST_28, B_TEST_2, A_TEST_27, A_TEST_1]

So thats all for Lambda expressions.

Next section we will be dealing with Exceptions.