13. What are the differences between Predicate, Consumer, and Function functional interfaces in Java 8?

Basic

13. What are the differences between Predicate, Consumer, and Function functional interfaces in Java 8?

Overview

In Java 8, functional interfaces like Predicate, Consumer, and Function play a pivotal role in enabling functional programming concepts. They are fundamental to the Streams API, simplifying the process of manipulating collections and fostering more concise and readable code. Understanding the differences and applications of these interfaces is crucial for writing effective Java applications.

Key Concepts

  • Predicate: Evaluates an object and returns a boolean.
  • Consumer: Performs an operation on an object without returning any result.
  • Function: Takes an object of one type and returns an object of another type.

Common Interview Questions

Basic Level

  1. Explain the differences between Predicate, Consumer, and Function interfaces in Java 8.
  2. Provide an example of using a Predicate to filter a list of integers.

Intermediate Level

  1. How can you use a Function interface to transform a List into a List representing their lengths?

Advanced Level

  1. Discuss a scenario where combining Consumer, Predicate, and Function interfaces would be beneficial. Provide an example implementation.

Detailed Answers

1. Explain the differences between Predicate, Consumer, and Function interfaces in Java 8.

Answer: Predicate, Consumer, and Function interfaces are part of Java 8's introduction of functional programming features. Each serves a distinct purpose:

  • Predicate is used for evaluating an expression that returns a boolean value. It's commonly used for filtering data.
  • Consumer performs an operation on a single input argument without returning any result. It's often used for iterating over collections to apply operations.
  • Function accepts one argument and produces a result. This makes it useful for mapping scenarios, where you transform a value from one type to another.

Key Points:
- Predicate is used for conditional checks.
- Consumer is used for performing operations like printing or modifying elements.
- Function is used for transforming values from one form to another.

Example:

List<String> names = Arrays.asList("John", "Jane", "Doe", "Sarah");
names.forEach(name -> System.out.println(name)); // Consumer example

List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
List<Integer> squaredNumbers = numbers.stream().map(n -> n * n).collect(Collectors.toList()); // Function example

2. Provide an example of using a Predicate to filter a list of integers.

Answer: A Predicate can be used with a Stream to filter elements according to a specified condition. For example, filtering a list of integers to find only even numbers:

Key Points:
- Use stream().filter() to apply the Predicate.
- Predicate should return true for elements you want to keep.
- Collect the result using collect(Collectors.toList()).

Example:

List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6);
Predicate<Integer> isEven = n -> n % 2 == 0; // Predicate to check even numbers

List<Integer> evenNumbers = numbers.stream().filter(isEven).collect(Collectors.toList());
System.out.println(evenNumbers); // Will print [2, 4, 6]

3. How can you use a Function interface to transform a List into a List representing their lengths?

Answer: The Function interface can be used to map each string in a list to its length. This is accomplished using the map() method provided by the Stream API, transforming each element according to the provided Function.

Key Points:
- Use stream().map() to apply the Function.
- The Function should return the new form of each element.
- Collect the result using collect(Collectors.toList()).

Example:

List<String> words = Arrays.asList("hello", "world", "java", "programming");
Function<String, Integer> wordLength = String::length; // Function to get string lengths

List<Integer> lengths = words.stream().map(wordLength).collect(Collectors.toList());
System.out.println(lengths); // Will print [5, 5, 4, 11]

4. Discuss a scenario where combining Consumer, Predicate, and Function interfaces would be beneficial. Provide an example implementation.

Answer: A common scenario is processing a collection of objects where you need to filter elements based on a condition (Predicate), transform them (Function), and then perform some operation on each of the transformed elements (Consumer). For example, filtering a list of employees by salary, increasing their salary, and then printing out their names and new salaries.

Key Points:
- Use Predicate for filtering.
- Use Function for transformation.
- Use Consumer for performing an operation on each element.

Example:

class Employee {
    String name;
    double salary;

    // Constructor, getters, and setters omitted for brevity
}

List<Employee> employees = // Assume this is initialized

Predicate<Employee> earnsOver50k = e -> e.getSalary() > 50000;
Function<Employee, Employee> giveRaise = e -> { e.setSalary(e.getSalary() * 1.1); return e; };
Consumer<Employee> printNameAndSalary = e -> System.out.println(e.getName() + ": " + e.getSalary());

employees.stream().filter(earnsOver50k).map(giveRaise).forEach(printNameAndSalary);

This code filters employees who earn more than 50k, gives them a 10% raise, and then prints their name and new salary.