Lambda expressions introduced in Java 8 have changed the syntax drastically and speeded up the way we code. I can fell like it was yesterday but almost 5 years have passed since its first run. Here, I will show you a few basic terms and code snippets so we can dive into the topic.
Lambda expressions are methods that can be treated like objects. We were given method references which can be applied where the l. expression is needed.
Functional Interface – basically an interface that has only one abstract functional method (not a default one). Let’s take a look at the example of the well known Runnable interface.
It has only one abstract method run() so any class that needs to run in separate thread has to implement that interface and has to be passed as a paramter (as one way of running thread in Java). The ‘classic’ approach produces a lot of code (but that’s why we love java, isn’t it) – and usually we used anonymous classes:
Thread thread = new Thread(new Runnable() { @Override public void run() { for (int i = 0; i < 5; i++) { System.out.println(i + ". That's an anonymous class"); } } }); thread.run();
That’s a correct way of doing that but along with Java 8 we got a quicker one:
Thread thread = new Thread(() -> { for (int i = 0; i < 5; i++) { System.out.println(i + ". That's a lambda expression!"); } }); thread.run();
We can see that the code that is executed is separated form the argument list (in this case the run() method takes no parameters thus is just ()) with an arrow ->. The l. expression has to return the same type as the method within the functional interface and take as many parameters – the signature must match. It means that the expression is the implementation of the method’s interface. Here we can see that we can assign the expression to a variable:
Thread thread; Runnable theMethod = () -> { for (int i = 0; i < 5; i++) { System.out.println(i + ". That's a lambda expression!"); } }; thread = new Thread(theMethod); thread.run();
Static method references
As we can make a reference to the method, we can also pass it as a paremeter.
Let’s say we have method reference like this – we refer to the static method within the SecondMain class using :: operator.
package app; import java.util.Arrays; import java.util.List; import java.util.function.Consumer; public class SecondMain { public static void main(String[] args) { // we create a method reference Consumer<String> theMethodReference = SecondMain::checker; // then the list of strings List<String> theList = Arrays.asList("hello", "these", "are", "the", "list", "tomato"); // passing the method as a reference theList.forEach(theMethodReference); System.out.println("\nOther way of doing this with the same results: "); // or we can put it like this - we reference to the class and call the static method after :: signs theList.forEach(SecondMain::checker); } private static void checker(String val) { if (val.matches("tomato")) { System.out.println("This is a tomato!"); } else { System.out.println("This is not a tomato!"); } } }
Output:
This is not a tomato! This is not a tomato! This is not a tomato! This is not a tomato! This is not a tomato! This is a tomato! Other way of doing this: This is not a tomato! This is not a tomato! This is not a tomato! This is not a tomato! This is not a tomato! This is a tomato!
So these are the basics, let’s move on to some more complex examples of using lambdas.