menu sluiten
Contact

Amsterdam
Pedro de Medinalaan 87a/b, 1086 XP Amsterdam
Nederland +31 (0)85-888 33 31

Breda
Neerloopweg 36, 4814 RS Breda
Nederland +31 (0)85-888 33 31

Antwerpen
Veldkant 33B, 2550 Kontich
België +32 (0)3 444 11 08

info@jstack.eu

14 mei 2016

Lambda functions: an introduction

In this blog, I would like to explain how Lambda functions work in Java, and how we can use them. In blogs to come, I’ll explain how they are fitted into the Files, Stream and Collection API of Java 8.

Functional interface

When talking about Lambda’s , the first thing to understand is the Functional Interface. As this is the sort of interface you would be using, when using Lambda’s. In fact Lambda’s define the implementation of a functional interface.

Java versions prior to 8 had SAM interfaces : Single Abstract Method-interfaces. As the definition speaks for itself, it’s an interface with 1 abstract method. In Java 8, the concept of SAM-interfaces has been re-created and called “functional interfaces”.

You can write a functional interface yourself, but some already provided by the Java language via the java.util.function package, and used in different API’s.

Definition

A functional interface is an interface with exactly 1 abstract method. The interface can have other methods, but they would have to be default methods, meaning that they would have their implementation in the interface itself. (This is also a new feature in Java 8.)

Example

When writing a functional interface yourself, it’s a good practice to annotate it with @FunctionalInterface. This annotation is used to generate compiler level errors when the interface has more than 1 abstract method in your interface. The annotation is not required, but improves the readability of your code. For example:

1
2
3
4
@FunctionalInterface
interface Calculator {
public double calculate(double val1, double val2);
}

If you would add another abstract method, you would get a compile time error:

Invalid ‘@FunctionalInterface’ annotation; MyCalculator is not a functional interface

We could add a few default method’s, and the interface will still remain a functional interface:

1
2
3
4
5
6
7
8
9
10
@FunctionalInterface
interface Calculator {
   public double calculate(double val1, double val2);
   default public void print(String str) {
      System.out.println("The print method prints "+str);
   }
   default public void print(double val1, double val2) {
      System.out.println("The print method prints "+val1+" "+val2);
   }
}

So we have written a functional interface with a method that takes a 2 doubles and returns a double. Now let’s create an implementation for this interface, the ‘old school’ way:

1
2
3
4
5
6
7
8
9
10
Calculator multiply = new Calculator() {
    public double calculate(double val1, double val2) {
            return val1 * val2;
    }
};
Calculator sum = new Calculator() {
    public double calculate(double val1, double val2) {
            return val1 + val2;
    }
};

With Lamba functions it looks like this:

1
2
Calculator multiply = (val1, val2) -> val1 * val2;
Calculator sum = (val1, val2) -> val1 + val2;

Notice that we removed the double declaration of the parameters, as the java compiler can defer those from the interface.

Now let’s use our classes:

1
System.out.println("multiply = "+multiply.calculate(5,6));

This line executes 5*6, so it prints out “multiply = 30.0”

1
System.out.println("sum = "+sum.calculate(5,6));

his line executes 5+6, so it prints out “sum = 11.0”

The Java API way

The java.util.function package contains functional interfaces that are used throughout the Java API. I’ll explain the Function, Consumer, Supplier, and Predicate as those are the ones that are mostly used in the Java API. We’ll use them in the next chapter where we demonstrate how to use them in the Stream API, an Java API that makes a lot of use of Lambda’s.

Function

So let’s start exploring the possibilities and usages of the lambda function’s in Java. As stated before, the java.util.function packages contains functional interfaces, and in this chapter I’d like to explain how to use them.
If you look at the abstract method of the Function interface, you see It looks like this :
R apply(T t)
This method does nothing more then take an argument and returns a result. So we could write the following:

1
2
Function plus5 = (i -> i+5);
Integer var1 = plus5(1);

This function class will take an Integer, and return that integer+5. As we saw that the apply is the abstract function, i+5 is the implementation for that function. So that means that
Line 2 will set var1 to 6.
When we want more than 1 line code in the lamda function, we’ll need to use curly brackets.

1
2
3
4
5
Function plus5 = (i ->  { 
                System.out.println(" Integer=" + i);
               return i+5; 
});
Integer var1 = plus5(1);

If we execute the code on the last line, it will first, print ‘5’ to the console, and then, set integer var1 to 6.
Now that we know how to use a Function, let’s have a look at another interesting method in this interface :
default Function andThen(Function after)
This is a default method, meaning that it has its implementation in the Function interface class. Using this method, we can execute multiple functions in a chain. This is well illustrated when adding a print out in the Lamda.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
Function plus5 = (i ->  { 
                System.out.println("Before plus 5 = " + i);
               return i+5;
         });
Function min2 = (i ->  { 
                System.out.println("Before min2 " + i);
               return i-5;
         });
Function plus10 = (i ->  { 
                System.out.println("Before plus10 " + i);
               return i+10;
         });
Integer var1 = plus5.andThen(min2).andThen(plus10).apply(100);
System.out.println("var1="+var1);

When executing this code, we’ll see the following output on the console :
Line 1 : before plus 5 = 100
Line 2 : before min2 105
Line 3 : before plus10 103
Line 4 : var1=113
So we execute plus5 with value 100, which prints Line 1.
andThen we execute min2 method (Line 2), entered with the new value 105 (5 added by the apply method)
andThen we execute plus10 method (Line 3), entered with the new value 103 (minus 2 by the first andThen method)
final result is 113, as 10 was added to 103 by the plus10.

Consumer

The java.util.function.Consumer interface is a bit like the Function, only it doesn’t returns any values. It just ‘consumes’ an object. Looking at the API, the abstract method is the following :
void accept (T t)
So using our Lambda’s here:

1
2
Consumer printConsumer = str ->  System.out.println("str =" + str);
printConsumer.accept(“Hello Lambda”);

This will print
str=Hello Lambda
As you can see, we give the consumer a String as input, and it does something with it : print it out. As Function, the Consumer also has the ability to chain actions using a ‘andThen’ method:

1
2
3
4
Consumer c1 = i ->  System.out.println("C1 Integer =" + i);
Consumer c2 = i ->  System.out.println("C2 Integer =" + (i+1));
Consumer c3 = i ->  System.out.println("C3 Integer =" + (i+5));
c1.andThen(c2).andThen(c2).andThen(c3).andThen(c2).accept(100);

This will print out the following to the console :
C1 Integer =100
C2 Integer =101
C2 Integer =101
C3 Integer =105
C2 Integer =101

First the accept of c1 is executed, then twice c2, adding 1 each time then C3, adding 5, and after c3 , c2 is executed again, adding 1.

Supplier

Next, we have the Supplier. This interface does not take any parameters, but only produces an output result.
Looking at the API we see following abstract method :
T get()
Now here’s an example of a supplier, that supplies us with a random number:

1
2
Supplier random = () -> new Random().nextInt();
System.out.println("random integer:"+random.get());

This will print out the following to the console :
random integer:1020054503
As there is no parameter passed to the method , we use the syntax “()” before the arrow to indicate this.

Predicate

Finally there is the Predicate interface. The abstract method here is
boolean Test(T t)
So it takes an object, and returns a primitive Boolean.

1
2
Predicate equals5 = i -> i==5;
System.out.println("Is equal to 5 ? "+equals5.test(7));

This will print out the following to the console :
Is equal to 5 ? false
Now we can also use this in a chain with ‘and’ or ‘or’:

1
2
Predicate equals10 = i -> i==10;
System.out.println("Is equal to 5 or 10 ? "+equals5.or(equals10).test(5));

This will print out the following to the console :
Is equal to 5 or 10 ? true

Conclusion

So in this article, I explained the very basics of Lambda functions. If you understand this, you’re ready for the great Lambda experience. Be prepared for more blogs to come on this subject.

Meer weten?

Neem contact op met Arne. Hij denkt graag met u mee in een persoonlijk gesprek.

info@jstack.eu +31 (0)85-888 33 31

Interessant? Deel dit artikel met een vriend(in) of collega!

Vragen over dit onderwerp?

Onze experts denken graag met u mee


Gerelateerde berichten