In this blog, I would like to explain how Lambda functions work in Java, and how we can use them.
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:
If you would add another abstract method, you would get a compile time error: Invalid ‘@FunctionalInterface’ annotation; MyCalculator is not a functional interface@FunctionalInterface interface Calculator { public double calculate(double val1, double val2); }
We could add a few default method’s, and the interface will still remain a functional interface:
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:@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); } }
With Lamba functions it looks like this: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; } };
Notice that we removed the double declaration of the parameters, as the java compiler can defer those from the interface.Calculator multiply = (val1, val2) -> val1 * val2; Calculator sum = (val1, val2) -> val1 + val2;
Now let’s use our classes:System.out.println("multiply = "+multiply.calculate(5,6)); This line executes 5*6, so it prints out “multiply = 30.0” System.out.println("sum = "+sum.calculate(5,6)); this line executes 5+6, so it prints out “sum = 11.0”
-
The Java API way
Thejava.util.function
package contains functional interfaces that are used throughout the Java API. I’ll explain theFunction
,Consumer
,Supplier
, andPredicate
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.
1.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:
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.
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.
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.
2.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:
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:
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.
3.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:
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.
4.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.
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
:
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.
This is copied by Hugo from https://www.jstack.eu/blog. And also I don't wanna to translate it.