Java 8 Interview Questions – Lambdas

1. What is a lambda expression in Java 8?

Answer:
A lambda expression is a feature introduced in Java 8 that allows you to define and use anonymous functions. It provides a concise way to represent instances of functional interfaces (interfaces with a single abstract method) as instances of functional programming concepts. Lambda expressions are particularly useful for simplifying code when working with collections and streams.

Example:

Java
   // Lambda expression to sort a list of strings
   List<String> names = Arrays.asList("John", "Alice", "Bob");
   Collections.sort(names, (a, b) -> a.compareTo(b));

2. What is a functional interface in Java 8, and why is it important for lambda expressions?

Answer:
A functional interface is an interface that has only one abstract (unimplemented) method. Java 8 introduced the concept of functional interfaces to provide a single abstract method for lambda expressions to target. Functional interfaces are important for lambda expressions because they define the contract that lambda expressions must adhere to. They enable type checking and make it possible for the Java compiler to infer the type of lambda expressions.

Example:

Java
   // Functional interface representing a binary operation
   interface BinaryOperation {
       int perform(int a, int b);
   }

3. How is a lambda expression represented in Java, and what is the syntax for defining one?

Answer:
A lambda expression in Java is represented as a block of code enclosed within parentheses and an arrow (->) that separates the parameter list from the body of the lambda. The syntax for defining a lambda expression is:

Java
   (parameter1, parameter2, ...) -> { /* Lambda body */ }
  • parameter1, parameter2, ...: Parameters are optional, but if there are multiple parameters, they must be enclosed in parentheses.
  • ->: The arrow separates the parameter list from the lambda body.
  • { /* Lambda body */ }: The lambda body contains the code to be executed when the lambda is invoked. Example:
Java
   // Lambda expression to add two numbers
   BinaryOperation addition = (a, b) -> a + b;
   int result = addition.perform(5, 3); // Result is 8

4. What is the target type of a lambda expression in Java, and how does it determine the appropriate functional interface?

Answer:
The target type of a lambda expression is the type to which the lambda expression is being converted. In Java, a lambda expression can only be used in a context where its target type is known. The target type determines the appropriate functional interface that the lambda expression must match.

For example, if you assign a lambda expression to a variable of a functional interface type, the lambda expression must have the same parameter types and return type as the single abstract method of that interface.

Example:

Java
   // Target type is BinaryOperation
   BinaryOperation addition = (a, b) -> a + b;

   // Target type is Runnable (no parameters or return type)
   Runnable runnable = () -> System.out.println("Hello, Lambda!");

5. How do you use lambda expressions to iterate over a collection in Java 8?

Answer:
In Java 8, you can use the forEach method provided by the Iterable interface to iterate over a collection using a lambda expression. This method takes a Consumer functional interface, which represents an operation that accepts an input and returns no result.

Example:

Java
   List<String> names = Arrays.asList("John", "Alice", "Bob");

   // Using lambda expression to iterate and print each element
   names.forEach(name -> System.out.println(name));

The lambda expression (name -> System.out.println(name)) represents the action to be performed on each element of the collection.

6. What are method references in Java 8, and how do they relate to lambda expressions?

Answer:
Method references are a shorthand notation for calling methods using lambda expressions. They provide a way to reference methods directly and pass them as arguments to functions or methods. Method references are closely related to lambda expressions because they often replace lambda expressions when the lambda’s only action is to call a method.

There are four types of method references in Java 8:

  • Reference to a static method: ClassName::staticMethodName
  • Reference to an instance method of a particular object: object::instanceMethodName
  • Reference to an instance method of an arbitrary object of a particular type: ClassName::instanceMethodName
  • Reference to a constructor: ClassName::new Example (Method Reference):
Java
   List<String> names = Arrays.asList("John", "Alice", "Bob");

   // Using a method reference to print each element
   names.forEach(System.out::println);

7. What is the purpose of the java.util.function package in Java 8, and how does it relate to lambda expressions?

Answer:
The java.util.function package in Java 8 contains a set of functional interfaces that represent common function types like predicates, consumers, suppliers, and functions. These functional interfaces provide a standardized way to work with lambda expressions and method references. The package is essential for lambda expressions because it defines many common functional interfaces that can be used as target types for lambdas.

Some commonly used functional interfaces from this package include:

  • Consumer<T>: Represents an operation that accepts a single input and returns no result.
  • Supplier<T>: Represents a supplier of results.
  • Predicate<T>: Represents a predicate (boolean-valued function) of one argument.
  • Function<T, R>: Represents a function that takes one argument and returns a result.

8. How do you create a custom functional interface in Java 8, and what are the rules for creating one?

Answer:
To create a custom functional interface in Java 8, you define an interface with a single abstract method (SAM). The rules for creating a functional interface are as follows:

  • The interface must have exactly one abstract method.
  • It can have multiple default or static methods.
  • It can extend other interfaces, even if they have multiple abstract methods, as long as those methods have the same name and parameter types. Example:
Java
   @FunctionalInterface
   interface CustomFunctionalInterface {
       void performAction(int value);

       // This is allowed since it's a default method
       default void doSomethingElse() {
           // Implementation here
       }
   }

The @FunctionalInterface annotation is optional but helps indicate the intended use of the interface as a functional interface.

9. What is the purpose of the Stream API in Java 8, and how do lambda expressions play a role in it?

Answer:
The Stream API in Java 8 is designed for working with sequences of elements in a functional style. It provides a powerful way to process data using a combination of filter, map, reduce, and other operations. Lambda expressions are crucial for the Stream API because they allow you to specify the behavior that should be applied to each element in a declarative and concise manner.

Example:

Java
   List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);

   // Using a stream and lambda expressions to filter and sum even numbers
   int sum = numbers.stream()
                   .filter(n -> n % 2 == 0)
                   .mapToInt(Integer::intValue)
                   .sum();

In this example, lambda expressions are used within the filter and mapToInt operations to define the processing steps.

10. How do you sort a collection of objects using lambda expressions and the Comparator interface in Java 8?

Answer: You can sort a collection of objects using lambda expressions and the `Comparator` interface in Java 8 by providing a custom comparison logic. The `Comparator` interface allows you to define how two objects should be compared. Example:

Java
List<Person> people = Arrays.asList(
    new Person("John", 30),
    new Person("Alice", 25),
    new Person("Bob", 35)
);

// Sorting people by age using a lambda expression
Collections.sort(people, (p1, p2) -> p1.getAge() - p2.getAge());

// Resulting order: Alice, John, Bob

In this example, a lambda expression is used within the `Comparator` to specify the sorting logic based on the `age` property.

11. What is the difference between a lambda expression and an anonymous inner class in Java 8?

Answer: Lambda expressions and anonymous inner classes serve similar purposes, but there are key differences:

Syntax: Lambda expressions have a more concise and readable syntax compared to anonymous inner classes.

Functional Interfaces: Lambda expressions can only be used with functional interfaces (single abstract method), while anonymous inner classes can implement multiple methods.

Variables: Lambda expressions can access local variables from their enclosing scope, but those variables must be effectively final or final. Anonymous inner classes can access final and non-final local variables.

Java
Runnable runnable = () -> System.out.println("Lambda Expression");
```

**Example (Anonymous Inner Class):**
```
Runnable runnable = new Runnable() {
    @Override
    public void run() {
        System.out.println("Anonymous Inner Class");
    }
};

12. What are the “effectively final” variables in Java 8, and why are they relevant to lambda expressions?

Answer: An “effectively final” variable in Java 8 is a variable that is not declared as `final` but is treated as if it were final by the compiler. In the context of lambda expressions, a local variable referenced within a lambda expression must be effectively final. This means that the variable should not be modified after it is captured by the lambda expression. This restriction ensures that lambda expressions do not cause unexpected behavior when capturing variables from their enclosing scope.

13. How do you capture variables from the enclosing scope within a lambda expression in Java 8?

Answer: In Java 8, lambda expressions can capture variables from their enclosing scope. To capture a variable, the variable must be effectively final or final. This means that the variable should not be modified after it is captured by the lambda expression. The lambda expression can then access and use the captured variable.

In this example, the variable `x` is captured by the lambda expression.

Java
int x = 10; // Captured variable
Consumer<Integer> consumer = (n) -> {
    System.out.println(n + x); // Accessing the captured variable
};

14. What is the “FunctionalInterface” annotation in Java, and how is it used with custom functional interfaces?

Answer: The `@FunctionalInterface` annotation in Java is used to indicate that an interface is intended to be a functional interface. A functional interface is an interface with a single abstract method (SAM). The annotation is not required, but it helps signal the intent of the interface and allows the compiler to check if it adheres to the functional interface contract.

Java
@FunctionalInterface
interface CustomFunctionalInterface {
    void performAction();
}

If you attempt to define more than one abstract method in an interface marked with @FunctionalInterface, the compiler will generate an error.

15. How do you use lambda expressions to parallelize operations on a collection using the parallelStream method in Java 8?

Answer: In Java 8, you can use the `parallelStream` method to parallelize operations on a collection, allowing for concurrent processing of elements. Lambda expressions play a role by providing the behavior that should be applied to each element during parallel processing.

Java
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);

// Using parallelStream with a lambda expression to perform concurrent processing
long count = numbers.parallelStream()
                   .filter(n -> n % 2 == 0)
                   .count();

In this example, the lambda expression (n -> n % 2 == 0) is applied concurrently to filter even numbers using parallel processing.

Leave a Comment

Your email address will not be published. Required fields are marked *

Scroll to Top