Describe a lambda expression; refactor the code that uses an anonymous inner class to use a lambda expression; describe type inference and target typing.
Lambda expressions are similar to anonymous classes, but only have the implementation of the methods. Therefore, they are like "anonymous methods" that can be passed via variables.
Understanding lambda functions, and variations in their syntax, is essential to understanding the next sections of Built-in Interfaces and Method References.
The lambda expression has 3 parts:
-
A comma-separated list of parameters
-
Sometimes has parentheses
-
Sometimes has variable type
-
-
The "arrow token", which is always written as
->
-
A body, which may or may not be enclosed in braces
-
Can have more than one line
-
Sometimes has a
return
-
Sometimes has semicolon
-
Examples of lambda expressions:
-
i → System.out.println(i)
-
(Integer i) → System.out.println(i)
-
(Integer i) → { System.out.println(i); }
-
(Integer i) → { return i + 1; }
-
(i, j, k) → { return i + j + k; }
-
(i, j, k) → System.out.println(i + j + k)
-
() → System.out.println("nothing")
-
Lambda expressions can be understood as a different way of declaring anonymous classes.
src/org/j6toj8/lambda/lambdaexpression/LambdaExpression_AnonymousClass.javalink:../../../src/org/j6toj8/lambda/lambdaexpression/LambdaExpression_AnonymousClass.java[role=include]
Note that in the example above a
Thread
is instantiated with an anonymous instance ofRunnable
. In the second part, the same thing is done much simpler using a lambda expression. -
Lambda expressions are always used to create instances of functional interfaces, i.e., interfaces that have only a single abstract method.
src/org/j6toj8/lambda/lambdaexpression/LambdaExpression_FunctionalInterface.javalink:../../../src/org/j6toj8/lambda/lambdaexpression/LambdaExpression_FunctionalInterface.java[role=include]
console outputperformed with anonymous class performed with lambda expression
Note that in the example above the same
executeAndPresentMessage
method is invoked twice. The first time a new anonymous class is passed. The second time a lambda expression is passed.Also see that it would be impossible to create a lambda expression if the interface was not functional, i.e., it had more than one abstract method. The compiler would not be able to identify that the
execute
method of the` Executable` interface is being implemented within the lambda expression. -
There are many methods already available in Java 8 that benefit from lambda expression syntax, such as
forEach
lists.src/org/j6toj8/lambda/lambdaexpression/LambdaExpression_ForEach.javalink:../../../src/org/j6toj8/lambda/lambdaexpression/LambdaExpression_ForEach.java[role=include]
console output1 2 3 4 5
Note that the new
forEach
method executes the lambda expression passed as a parameter to each list item, printing them all to the console. The lambda expression takes as a parameter a number, which is the list number.In this case, the functional interface being implemented by the lambda expression is called
Consumer
. It will be explained in detail in a later section, along with other standard Java 8 functional interfaces. In this section, it is essential to understand what lambda expressions are and what their syntax is like. -
Declarations of lambda expressions can be complete or simplified.
src/org/j6toj8/lambda/lambdaexpression/LambdaExpression_SimpleComplete.javalink:../../../src/org/j6toj8/lambda/lambdaexpression/LambdaExpression_SimpleComplete.java[role=include]
The two lambda functions above are identical, but one is more explicit than the other.
-
Parentheses can only be omitted if there is no type declaration, and there is only a single argument.
-
Lambda expressions that do NOT have arguments also need parentheses.
src/org/j6toj8/lambda/lambdaexpression/LambdaExpression_Parenthesis.javalink:../../../src/org/j6toj8/lambda/lambdaexpression/LambdaExpression_Parenthesis.java[role=include]
-
The use of curly brackets, semicolons and
return
(if the function returns any value) is required in lambda expressions with more than one line.src/org/j6toj8/lambda/lambdaexpression/LambdaExpression_Block.javalink:../../../src/org/j6toj8/lambda/lambdaexpression/LambdaExpression_Block.java[role=include]
-
When making the type of one of the arguments explicit, it is mandatory to inform all of them.
src/org/j6toj8/lambda/lambdaexpression/LambdaExpression_VarType.javalink:../../../src/org/j6toj8/lambda/lambdaexpression/LambdaExpression_VarType.java[role=include]
-
It is not allowed to declare variables with the same name within the lambda expression.
src/org/j6toj8/lambda/lambdaexpression/LambdaExpression_Shadowing.javalink:../../../src/org/j6toj8/lambda/lambdaexpression/LambdaExpression_Shadowing.java[role=include]
-
It is allowed to access external variables within the lambda expression, but only final variables or variables that do not change.
src/org/j6toj8/lambda/lambdaexpression/LambdaExpression_AccessExternalVar.javalink:../../../src/org/j6toj8/lambda/lambdaexpression/LambdaExpression_AccessExternalVar.java[role=include]
Note that the compiler identifies that the
x3
variable is changed at the end of the method, and therefore does not allow it to be used in the lambda expression. -
In ambiguous situations, the compiler tries to find out the type of lambda expression using the context.
src/org/j6toj8/lambda/lambdaexpression/LambdaExpression_TypeInference.javalink:../../../src/org/j6toj8/lambda/lambdaexpression/LambdaExpression_TypeInference.java[role=include]
In the previous example, only the
run
method of theApplication
functional interface returns aString
, while theexecute
method of theExecutable
functional interface returns nothing (void
). This is enough difference for the compiler to know which method to call each timedoThis
is invoked.On the first call to the
doThis
method, since the passed lambda expression returns aString
, the compiler understands that this lambda expression is an implementation of theApplication
interface, as therun
method also returns aString
.On the second call to the
doThis
method, since the lambda expression returns nothing (just prints theString
"executing"
), the compiler understands that this lambda expression is an implementation of theExecutable
interface, because theexecute
also returns nothing. -
If the type of the lambda expression cannot be found, a compilation error occurs.
src/org/j6toj8/lambda/lambdaexpression/LambdaExpression_Ambiguity.javalink:../../../src/org/j6toj8/lambda/lambdaexpression/LambdaExpression_Ambiguity.java[role=include]
In the previous example, because both functional interfaces have a
void
method, the compiler does not know which one is being instantiated in the lambda expression, and a compilation error occurs. The lambda expression in this example could be eitherPilot
orRunner
, and there is no way for the compiler to find out which developer actually wanted to use it.
-
Implementing Functional Interfaces with Lambdas
Boyarsky, Jeanne; Selikoff, Scott. OCP: Oracle Certified Professional Java SE 8 Programmer II Study Guide (p. 55). Wiley. Kindle Edition.
-
Using Variables in Lambdas
Boyarsky, Jeanne; Selikoff, Scott. OCP: Oracle Certified Professional Java SE 8 Programmer II Study Guide (p. 172). Wiley. Edição do Kindle.
-
Lambda Expressions and Functional Interfaces: Tips and Best Practices.
-
Lambda Expressions. The Java™ Tutorials.