chapter 15

Chapter 15: Lambda Expressions in Java

Introduction to Lambda Expressions

  • Lambda expressions are a key feature of Java, introduced to facilitate functional programming.

  • Contains two core constructs: Lambda expressions and Functional Interfaces.

  • Definition: A lambda expression is an anonymous method, effectively an unnamed function used to implement the defined method of a functional interface.

  • Lambda expressions can also be referred to as closures.

Functional Interfaces

  • Definition: A functional interface is an interface that contains exactly one abstract method, defining the intended purpose and representing a single action.

  • Example: The Runnable interface is a functional interface because it has only one method: run(), representing its action.

  • A lambda expression can only be utilized in a context where its target type is defined by a functional interface.

  • Functional interfaces are sometimes known as SAM types (Single Abstract Method).

Syntax and Structure of Lambda Expressions

  • Lambda expressions introduce a new syntax and operator in Java denoted by ->.

  • The structure consists of two parts:

    • Left Side: Specifies parameters required (can be empty if none).

    • Right Side: Contains the action to be performed.

  • The operator can be read as "becomes" or "goes to." There are two types of lambda bodies:

    1. Single Expression

    2. Block of Code

  • Example of a simple expression: () -> 123.45 (equivalent to a method that returns 123.45).

  • Example with Math: () -> Math.random() * 100, obtaining a pseudo-random value multiplied by 100.

Working with Parameters

  • When parameters are involved, they are listed on the left of the operator:

    • Example: (n) -> (n % 2) == 0 returns true if n is even.

  • Type inference allows parameters to be defined implicitly based on context.

  • Lambda expressions can have multiple parameters as needed but need to declare types explicitly if they are more than one.

Usage of Functional Interfaces

  • Lambda expressions need to be assigned to a functional interface reference, establishing a target type context.

  • Example:

    MyNumber myNum;  
    myNum = () -> 123.45;  
    • This forms an anonymous class that implements the functional interface, allowing lambda behavior.

  • When getValue() is invoked, it executes the lambda expression returning the specified value.

Complex Lambda Examples

  • Type Inference in Lambda: When a type is not specified, it can often be inferred:

    • Example: (n) -> (n % 2) == 0 allows type inference based on method usage.

  • When declaring types, all parameters must have types specified; mixing inferred and explicit type specifications is illegal.

    • Example legal: (int n, int d) -> (n % d) == 0 is valid.

Block Lambda Expressions

  • Block lambdas consist of a block of code enclosed in braces ({}).

  • Allows significant versatility, including variable declaration, loops, and conditional statements.

  • Must return values explicitly using the return statement.

  • Example: A block lambda reversing a string.

  • A block lambda has more complexity and utility over expression lambdas.

Handling Exceptions in Lambda Expressions

  • Lambda expressions can indeed throw exceptions; however, checked exceptions must be compatible with the throws clause of their functional interface's abstract method.

  • Example: A block lambda calculating an average, throwing EmptyArrayException for a zero-length array.

Variable Capture in Lambda

  • Local variables captured in lambdas must be effectively final.

  • Modifying the local variable after its assignment would disallow its use in lambda expressions.

  • Example: Attempting to modify an effectively final variable inside a lambda leads to compilation errors.

Method References

  • Method references allow referencing methods directly without executing them.

  • This relates to lambda expressions, as both require a functional interface context.

  • Various methods of referencing include referencing static methods, instance methods, and using generics.

Predefined Functional Interfaces

  • Java provides several built-in functional interfaces within the java.util.function package, reducing the necessity to define custom interfaces.

  • Example Implementation: The Function interface can compute factorials and other operations without needing distinct functional interfaces every time.

Conclusion

  • The chapter effectively introduces and elaborates on lambda expressions, functional interfaces, their syntax, operational examples, and the handling of exceptions in the context of Java programming. The application of lambda expressions integrates functional programming techniques, making Java a more versatile and modern programming language.