Skip to content

Latest commit

 

History

History
182 lines (156 loc) · 8.09 KB

02-lambda-expression.asc

File metadata and controls

182 lines (156 loc) · 8.09 KB

Lambda Expression

Objective
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:

  1. A comma-separated list of parameters

    • Sometimes has parentheses

    • Sometimes has variable type

  2. The "arrow token", which is always written as ->

  3. 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")

  1. Lambda expressions can be understood as a different way of declaring anonymous classes.

    src/org/j6toj8/lambda/lambdaexpression/LambdaExpression_AnonymousClass.java
    link:../../../src/org/j6toj8/lambda/lambdaexpression/LambdaExpression_AnonymousClass.java[role=include]

    Note that in the example above a Thread is instantiated with an anonymous instance of Runnable. In the second part, the same thing is done much simpler using a lambda expression.

  2. 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.java
    link:../../../src/org/j6toj8/lambda/lambdaexpression/LambdaExpression_FunctionalInterface.java[role=include]
    console output
    performed 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.

  3. 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.java
    link:../../../src/org/j6toj8/lambda/lambdaexpression/LambdaExpression_ForEach.java[role=include]
    console output
    1
    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.

  4. Declarations of lambda expressions can be complete or simplified.

    src/org/j6toj8/lambda/lambdaexpression/LambdaExpression_SimpleComplete.java
    link:../../../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.

  5. Parentheses can only be omitted if there is no type declaration, and there is only a single argument.

  6. Lambda expressions that do NOT have arguments also need parentheses.

    src/org/j6toj8/lambda/lambdaexpression/LambdaExpression_Parenthesis.java
    link:../../../src/org/j6toj8/lambda/lambdaexpression/LambdaExpression_Parenthesis.java[role=include]
  7. 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.java
    link:../../../src/org/j6toj8/lambda/lambdaexpression/LambdaExpression_Block.java[role=include]
  8. 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.java
    link:../../../src/org/j6toj8/lambda/lambdaexpression/LambdaExpression_VarType.java[role=include]
  9. It is not allowed to declare variables with the same name within the lambda expression.

    src/org/j6toj8/lambda/lambdaexpression/LambdaExpression_Shadowing.java
    link:../../../src/org/j6toj8/lambda/lambdaexpression/LambdaExpression_Shadowing.java[role=include]
  10. 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.java
    link:../../../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.

  11. In ambiguous situations, the compiler tries to find out the type of lambda expression using the context.

    src/org/j6toj8/lambda/lambdaexpression/LambdaExpression_TypeInference.java
    link:../../../src/org/j6toj8/lambda/lambdaexpression/LambdaExpression_TypeInference.java[role=include]

    In the previous example, only the run method of the Application functional interface returns a String, while the execute method of the Executable functional interface returns nothing (void). This is enough difference for the compiler to know which method to call each time doThis is invoked.

    On the first call to the doThis method, since the passed lambda expression returns a String, the compiler understands that this lambda expression is an implementation of the Application interface, as the run method also returns a String.

    On the second call to the doThis method, since the lambda expression returns nothing (just prints the String "executing"), the compiler understands that this lambda expression is an implementation of the Executable interface, because the execute also returns nothing.

  12. If the type of the lambda expression cannot be found, a compilation error occurs.

    src/org/j6toj8/lambda/lambdaexpression/LambdaExpression_Ambiguity.java
    link:../../../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 either Pilot or Runner, and there is no way for the compiler to find out which developer actually wanted to use it.

References
  • 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.