Java Lambda Expressions

aditya goel
6 min readSep 27, 2020

--

This wiki details about the lambda expressions introduced in Java8.

Lets begin with understanding, how the need for lambdas arises. To start with fundamental coding approach for doing some stuff till java7, using Polymorphism, here is how we shall be doing :-

Step.1) Create an Interface for the work :-

public interface Wisher {
public void perform();
}

Step.2) Create an solid implementation for aforesaid interface:-

public class HelloWorldWisher implements Wisher {
@Override
public void perform() {
System.out.println("Hello there, Good wishes !!");
}
}

Step.3) Create an testing class :-

public class MainTester {

public void wishesParser(Wisher wisher) {
wisher.perform();
}

public static void main(String args[]) {
MainTester mainTester = new MainTester();
HelloWorldWisher helloWorldWisher = new HelloWorldWisher();
mainTester.wishesPrinter(helloWorldWisher);
}
}

In above snippet, we are passing the object of type ‘HelloWorldWisher’ that has the behaviour stored in it. In other words, we are passing an class’s object which has action in it. Lambdas eliminates the need for this extra coding. With lambdas, we can treat the methods as values. With lambdas, we wish to assign the block of code to some variable like demonstrated below in code snippet :-

Function as Value :- (Below system is possible using Lambda expressions)

aPieceOfCode =  public void perform() {
System.out.println("Hello there, Good wishes !!");
}

Equivalent Lambda expression for the aforesaid snippet (we omitted classifier, methodName, returnType and curly-braces since its a 1 liner function):-

aPieceOfCode =  () -> System.out.println("Good wishes !!");

To read the above snippet, it takes no arguments and the body of this function is just to print the message. In other words, we have encapsulated the function in a variable which can be passed around and used. Pl note, we shall soon declare the type of this variable as well in further reading. As of now, its a variable which is capable of holding the function. Also, we can use the above lambda expression as inline as well i.e. without the variable-name being used. For example in context of aforesaid example, compiler is capable enough of doing the Type-Inference automatically and thus, below code snippet works absolutely fine :-

mainTester.wishesPrinter(() -> System.out.println("Lambda way of Wishing."));

Here are some more examples of Lambda functions :-

1.) Lambda expression to multiply any given number with 3. Here, we don’t have to specify the ‘return’ keyword in case of single line method.

TripleMultiplicator tripleMultiplicator =  (int a) -> a * 3;

OR

MultiplierWith3 multiplierWith3 =  (int a) -> a * 3;

OR

Function<Integer, Integer> mulBy3 =  (int a) -> a * 3;

Type of the above Lambda expression :- It just needs to infer that, there exists some interface with name as ‘TripleMultiplicator’ who have a method with anyXYZ name. This method receives a variable of type integer and also this function’s return-type is integer. Its only the definition that we have defined inline. Pl be informed that, compiler automatically does the type matching under the hoods.

public interface TripleMultiplicator {
public int anyFunctionNameIsFine(int a);
}
TripleMultiplicator tripleMultiplicator = (int a) -> a * 3;

Now, Please note here that, even if we have our interface something like below, still our lambda expression shall work fine :-

public interface MultiplierWith3 {
public int anyFunctionNameIsFine(int a);
}
MultiplierWith3 multiplierWith3 = (int a) -> a * 3;

Bottom-line is this interface creation is absolutely optional and we can rather use the inline definitions directly.

Another important point to note here is that, the optional interface that we have created above MUST contain only 1 method, otherwise, compiler would get confused and shall throw an compile time error.

Also, Java out of the box provides us many inbuilt interfaces like Predicate, Supplier, Function, BiConsumer, BiPredicate etc. which can be used as it is and therefore it eliminates our need to define these interfaces manually. Example, Java provides following :-

public interface Function<T, V> {
public V apply(T t);
}
Function<Integer, Integer> mulBy3 = (int a) -> a * 3;

2.) Lambda expression to add 3 given numbers with 3. Here, we don’t have to specify the ‘return’ keyword in case of single line method.

AdditorMahashay additor =  (int a, int b, int c) -> a + b + c;public interface AdditorMahashay {
public int anyFunctionNameIsFine(int a, int b, int c);
}

3.) Lambda expression to do safe division of 2 numbers. In case of multi-line method, its absolutely fine to use the curly braces and ‘return’ keyword.

DivisorHeisenberg divisor =  (int a, int b) -> {
if (b ==0) return 0;
return a/b;
}
public interface DivisorHeisenberg {
public int anyFunctionNameIsFine(int a, int b);
}

4.) Lambda expression to find length of string :- Below expression takes in a String type of parameter and returns the length of the string back to the caller method.

LengthFinder lenOfString =  (String s) -> s.length();public interface LengthFinder {
public int anyFunctionNameIsFine(String a);
}

Difference b/w Lambda and Interface implementations :-

Wisher helloWorldWisher = new HelloWorldWisher();

Vs

Wisher lambdaWisher = () -> System.out.println("Lambda wishes !!");

Vs.

Wisher innerClassWisher = new Wisher() {
@Override
public void perform() {
System.out.println("Inner Class way of Wishing.");
}
};

We know that, here Wisher is an interface having a single method with void return-type.

public interface Wisher {
public void perform();
}
helloWorldWisher.perform(); // Works fine.lambdadWisher.perform(); // Works fine as well.

In case of ‘helloWorldWisher’, we have declared the full-fledged class and that class have the behaviour implemented into it, whereas in case of ‘innerClassWisher’, its the creation of anonymous class first with using the name of the method and in case of ‘lambdaWisher’, we have declared the anonymous implementation of the ‘perform’ method directly without even using the name of the method.

Lambda way of coding is a shortcut and time-saver, but under the hoods, there is a lot functional context manipulation going on.

Utility of Lambda Expressions with Threading :-

Wherever we have a requirement of initiating a new anonymous class, we can use the Lambdas like below snippet :-

Thread anonymousClassWayOfThread = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("Inner Class way of Threading.");
}
});

anonymousClassWayOfThread.start();

Thread lambdaWayOfThread =
new Thread(() -> System.out.println("Lambda way of Threading."));
lambdaWayOfThread.start();

Another Utility of Lambda expressions example :-

In below code snippet :-

  • We are using a Java8 provided functional interface with name as ‘BiConsumer’. This interface have a built-in method with name as ‘accept’ which takes-in 2 parameters and returns nothing.
  • We are supplying the definition of the method i.e. implementation to be performed as an lambda expression. Here, we are suggesting to print the result of 2 integer numbers.
  • Invocation of the pre-provided method i.e. ‘accept’ happens from within a for loop.
public static void main(String args[]) {
int [] theseNumbers = {1, 2, 3, 4};
int key = 2;
process(theseNumbers, key, (v, k) -> System.out.println(v * k));
}

public static void process(int[] theseNumbers, int key, BiConsumer<Integer, Integer> biConsumer) {
for(int i : someNumbers) {
biConsumer.accept(key, i);
}
}

New stylus for iteration using Lambda way:-

Traditional way of iterating a list is :-

for (Integer num : someeNumbers) {
System.out.println(num);
}

and modern way of iterating the list with lambda way is as below. In this snippet, we are using lambda expression where in, we are passing one variable as input and doing nothing with it.

List<Integer> someeNumbers = Arrays.asList(1, 2, 3, 4, 5);
someeNumbers.forEach(thisNumber -> System.out.println(thisNumber));

Utility of Lambda in Stream processing :-

Below simple snippet explains about using the stream on numbers , filtering out only those numbers which are divisible by 2 fully and and then applying lambda to print them.

List<Integer> someeNumbers = Arrays.asList(1, 2, 3, 4, 5);
someeNumbers.stream().filter(thisNum -> (thisNum%2 == 0)).forEach(thisNumber -> System.out.println(thisNumber));

Please note here that, ‘filter’ method of stream takes in ‘Predicate’ type of functional interface as an argument. And here is how this functional interface looks like (as provided by Java out of the box). It takes in one parameter and returns the boolean type of response. And thats what exactly, we have passed in as a lambda expression. Basically, we are passing the definition of the function as an argument.

@FunctionalInterface
public interface Predicate<T> {
boolean test(T t);
}

References :-

--

--