Ever since I heard Java 8 is going to support lambda expressions (aka closures), I was very enthusiastic to spice my code with such a decent and concise functional element. Most developers extensively use Anonymous Inner Classes for event handlers, comparators, basic thread/runnable implementations, etc and overburden the logic with unnecessary scaffoldings even a very simple piece of code seems complex and unwieldy. Java 8 now added Lambda expressions as part of language syntax that help solve similar problem in a very elegant manner.
It enable developers to encapsulate a single unit of behavior and pass it to other code. It’s like syntactic sugar for an anonymous class (with one method whose type is inferred) and is an object-less method. I prefer avoiding extensive theoretical material in this post, but before moving further towards understanding the syntax, structure and examples of lambdas, there is an important concept need attention.
Functional Interface
A Functional Interface (aka Single Abstract Method type or SMA) is any
interface that contains only one abstract method. But it may contain some static
or/and default methods. java.lang.Runnable
is
an example of a Functional Interface, as it have only one run()
method, which is abstract. Similarly ActionListener interface is also a
functional interface. Following is an example of user defined functional
interface.
1
2
3
4
|
interface
Worker() {
boolean
doWork();
}; |
Have a look at another templatized functional interface example:
1
2
3
4
|
interface
Operator {
TYPE operate(TYPE operand1, TYPE operand2);
} |
That’s it, as it’s a normal interface that just have one abstract method.
Although there is more to talk on functional interface specially java 8′s
provided packagejava.util.function
and
@FunctionalInterface
annotation but for now just focus on lambdas.
I will cover these topic in detail in a separate post.
Lambda Expressions
Lambda expressions, also known as closures, are anonymous methods that provide developers with a simple and compact means for representing behavior as data.
- Brian Goetz, Specification Lead for JSR 335
To easily understand the syntax of a lambda expression we first take a look at conventional Anonymous Inner Class.
1
2
3
4
5
6
7
|
new Runnable() {
public
void run() {
performWork();
}
}; |
Lambda expressions provide the remedy for clumsiness of an Anonymous Inner Class and convert above five lines into a single line like.
1
|
() -> performWork(); |
Syntax and Structure
So, standard syntax of lambda is as follows:
1
|
() -> some expression |
Or
1
|
(arguments) -> { body just like function } |
A lambda expression consists of the following three parts:
- A comma-separated list of formal parameters enclosed in parentheses.
12345
// Taking two integers and retuning their sum
(
int
x,
int
y) -> x + y
// lambda expression with single integer argument that returns its next integer value
(
int
x) -> {
return
x +
1
; }
You can omit the datatype of the parameters in a lambda expression.
1234// same lambdas without argument types
(x, y) -> x + y
(x) -> {
return
x +
1
; }
In addition, you can omit the parentheses if there is only one parameter.
12// single argument lambda without parentheses
x -> {
return
x +
1
; }
- The arrow token,
->
12345//Lambda taking no argument and returning a constant integer vale, 92
() ->
92
// taking a string as an argument and printing that on console
(String s) -> { System.out.println(s); }
- A body, which consists of a single expression or statement block. In the
expression form, the body is simply evaluated and returned.
12345
// for single statement body, no need to use braces and the return statement
x -> x +
1
// simple lambda with void return type
() -> System.out.println(“Hello World!”)
In the block form, the body is evaluated like a method body and a return statement returns control to the caller of the anonymous method.
Ok, we spend enough on syntax of lambda, let’s move to some real examples
Examples of Lambda Expressions
To easily understand the lambda expressions, let’s start with some basic
comparative examples with anonymous inner class. In the first example, we will
see the use of lambda Comparator
interface implementation. Assume
we have a Person
class with name property, and we constructed an
array of Person
objects, named persons.
1
2
3
4
5
6
7
8
9
10
11
|
Arrays.sort(persons, new
Comparator() {
@Override
public
int compare(Person first, Person second) {
return
first.getName().compareTo(second.getName());
}
}); // it’s a standard sort but interestingly rather than passing Comparator object, it’s taking a lambda expression Arrays.sort(persons,(first, second) -> first.getName().compareTo(second.getName())); |
Notice that five lines of code turned into a single line, that’s the beauty
of lambda over anonymous inner classes. Now let’s look at another example of
Runnable
implementation. And same is in the Runnable
case.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
Runnable printer = new
Runnable() {
@Override
public
void run() {
System.out.println( "Hello, I’m inside runnable class..." );
}
}; printer.run(); printer = () -> System.out.println( "Hello, I’m inside runnable lambda..." );
printer.run(); |
Writing lambda expression for user defined functional interface is also very
simple and easy. Following example uses Operator
custom interface
and stores lambda expression in a reference variable for the sake of
reusability;
1
2
3
|
Operator addition = (op1, op2) -> op1 + op2; System.out.println( "Addition result: "
+ addition.operate( 2 , 3 ));
|
For further understanding, there is another example of lambda expressions with statement block.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
interface
GenericOperator {
TYPE operate(TYPE ... operands);
} //lambda expression with statement block GenericOperator multiply = numbers -> { int
result = 0 ;
for ( int
num : numbers)
result *= num;
return
result;
}; System.out.println( "Multiplication result: "
+ multiply.operate( 2 , 3 , 4 ));
|
As demonstrated above, the block is just like a normal function block and lambdas are functional elements similar to methods, but with higher significance.
When to use Lambda expression
Lambdas are not actually the replacement of anonymous inner classes but a batter way to implement the single abstract method types. Both of them has their significance and are intended to use in their specific scenarios.
- Lambda expression are meant to implement a single unit of behavior that is intended to pass to other code.
- Its usable when just simple instance of functional interface is desired, without having a type, constructor and related stuff.
- On the other hand anonymous inner classes are used when new fields and functionality is required.
Lambda expressions and Anonymous inner classes
- Anonymous classes introduce next level of scoping, whereas lambda expression are just like an enclosing environment. New variables with same name as in their super scope are allowed in classes whereas lambdas throw error and don’t allow that. Because they doesn’t introduce next level of scoping, local variable and fields/methods are directly accessible from enclosing scope.
- Anonymous classes resolves
this
keyword to their own object, though lambda resolves it to enclosing class where it’s written. To access variables inside lambda from enclosing class, you can usethis
. - However, like anonymous classes, lambda expressions can only access local variables of the enclosing block that are final or effectively final. Access to non-final variables throws an error just like anonymous classes.
- Serialization of lambda expressions are allowed only if its parameters and
return type/target type are
Serializable
. Whereas its serialization is highly discouraged like anonymous classes. - Lambda expressions are compiled as private methods of enclosing class.
Internally
invokedynamic
is used to bind methods dynamically, that were introduced in java 7.
Source Code
An IntelliJ project containing the source files for the examples covered in the post.
source: http://zishanbilal.com/2014/05/08/java-8-lambda-expressions-examples/