Introduction to Lambdas in Java

In this lesson on Lambdas in Java, we will see how they work and how they can be used to write smart code and smaller programs. We will also try to understand what powerful constructs are offered by Lambdas.

Let us start our journey by seeing some nice examples.

What are Lambdas

Lambdas refers to anonymous functions in programming. They enable us to write quick throw away functions without providing a semantic name for them.



Want a quick example? Let us show how to construct a Lambda using a Functional Interface to check if a number is even:

Predicate<Integer> isEvenNum = num -> num % 2 = 0;
boolean answer = pred.test(4); // true

In this lesson, we will construct many more real-life examples to see how Lambdas help us to shorten our Java development team.

Sorting Collections

Now, any collection can be sorted using default Collections.sort(...) method. Let’s see an example:

List<String> languages = Arrays.asList("French", "German", "Hindi", "Sanskrit");
Collections.sort(languages, new Comparator<String>() 
{
    @Override
    public int compare(String a, String b) {
        return b.compareTo(a);
    }
});

Now, let us try to convert this example where Lambdas are used to construct the same functionality:

Collections.sort(languages, (String str1, String str2) -> {return str1.compareTo(str2);});

See how much shorter the code was. Actually, it can get even shorter as we can even skip providing the return keyword and the braces:

Collections.sort(languages, (String str1, String str2) ->b.compareTo(str1));

Java compile knows the data type so we can ignore them too:

Collections.sort(languages, (str1, str2) -> str2.compareTo(str1));

See how code grew smaller from five lines to just a single line.

Looping through Collections

Lambdas are also helpful when we need to iterate through the collections. We will see how we do it without Lambdas first:

List<String> languages = Arrays.asList("French", "German", "Hindi", "Sanskrit");
for(String lang: languages) 
{
  System.out.println(lang);
}

The for-loop can be modified for a single line:

languages.forEach(a -> System.out.println(a));
Wait for it, it can get even shorter:

languages.forEach(System.out::println);
Applying Event Handlers on Swing elements
As seen in the first example, Lambdas can be used to replace Anonymous class usage. We can use this syntax to shorten code for event handling as well:

// Using anonymous inner class
button.setOnAction(new EventHandler<ActionEvent>() {
  @Override
  public void handle(ActionEvent event) {
    System.out.println("My Button."); 
  }
});

Let’s see how this changes with the usage of the Lambdas:

button.setOnAction(event -> System.out.println("My Button."));

See how the work was done in a single line!

Making thread Runnables

Till now, we have created and ran a runnable with default syntax:

new Thread(new Runnable() 
{
  @Override
  public void run() {
System.out.println("Old way to create thread");
  }
}).start();

But with a Lambda, we can create a new Thread with a lot more shorter syntax as:

new Thread(() -> System.out.println("Newest way!!!")).start();

Here, let’s try to understand what just happened in the statement:

() -> System.out.println("Newest way!!!")

The left part of the arrow ‘->’ is input parameter, which, in this case is none. Right part is the lambda expression body.

Filtering Lists

Though we have studied an example with Collections already, let us construct a practical example, like filtering a list based on a condition. Like, getting unique elements from a list.

// Create List of square of all distinct numbers 
List<Integer> nums = Arrays.asList(12, 10, 43, 4, 28, 33, 28); 
List<Integer> unique = numbers.stream().map( i -> i*i)
  .distinct()
  .collect(Collectors.toList());

So, what happened above is:

  • We created a list of numbers which contains some duplicate elements
  • We streamed through the integers
  • Applied a condition in the Lambda body
  • Collected the results in another List

When not to use Lambdas

From the discussion, Lambdas might sound perfect but in fact they aren't. Read through the points:

  • The larger the lambda, it is more difficult to follow the logic surrounding it
  • It is OK to repeat simple lambdas, usually which are only single line but it's for sure better to create a named function for repeated logic
  • Naming a function adds meaning to your code. With Lambdas, it is not possible to name a functionality.
  • Nested lambdas are very hard to follow along.

Besides the syntactic sugar-coated nature of Lambdas in Java, they majorly differ in performance:

  • Anonymous classes have a very slight slower performance which can be considered as negligible
  • Lambdas have a slower performance when called often because they build a bigger stack when they’re called

This means Lambdas and Inner classes have use-cases as when to use what:
  • Use Lambdas for readability when a few milliseconds performance loss is not an issue
  • Use inner classes for often called, performance critical functions

Conclusion

In this lesson on Lambdas in Java 8, we discussed about what they are, how are they made, what use they are of and what purpose do they actually achieve. Also, we had a look at when not to use them which is a major concern. Use Lambda well to write cleaner and precise code. Finally, we compared Lambdas and Inner classes.