Relationship Between Lambda Expressions and Functional Interfaces in Java

 


In Java, lambda expressions and functional interfaces go hand in hand. Let’s break it down step by step:

 

What is a Functional Interface?

  • A functional interface is an interface that has exactly one abstract method.

  • These interfaces can be annotated with @FunctionalInterface (optional but recommended) to indicate their intent.

  • Example: The Runnable interface is a functional interface because it has only one abstract method: run().

 
What is a Lambda Expression?

  • A lambda expression is a concise way to write an implementation of a functional interface.

  • Instead of creating an anonymous class to implement the interface, you can write it using a lambda.

    

An anonymous class is an inline implementation of an interface or abstract class, using the following syntax:

 

java

Copy code

InterfaceName obj = new InterfaceName() {
// Implementation of interface methods
};


Example of Creating an Anonymous Class

Let’s create an interface and use an anonymous class to implement it:


Step 1: Define the Interface

 

interface Greeting {

void sayHello();
}

Step 2: Create an Anonymous Class

 

java

Copy code

public class Main {
public static void main(String[] args) {
    // Create an anonymous class implementing the Greeting interface
    Greeting greeting = new Greeting() {
        @Override
        public void sayHello() {
            System.out.println("Hello from an anonymous class!");
        }
    };

// Call the method
    greeting.sayHello();
}
}

Output

 

Hello from an anonymous class!

 

Real-World Example

Consider a button click handler in a GUI application. For instance, you want to handle the click event using an anonymous class.

 

java

Copy code

interface ClickListener {
void onClick();
}

public class ButtonExample {
public static void main(String[] args) {
    // Create an anonymous class for the ClickListener interface
    ClickListener buttonClickListener = new ClickListener() {
        @Override
        public void onClick() {
            System.out.println("Button clicked!");
        }
    };

// Simulate a button click
        buttonClickListener.onClick();
}
}

Output

 

Button clicked!

 

Key Points

  1. Compact Implementation: You don’t need to create a separate named class for simple, one-time use.

  2. Limitations:

    • Anonymous classes cannot have constructors.

    • They can only extend one class or implement one interface at a time.

  3. When to Use:

    • Use anonymous classes for simple, temporary implementations.

    • Avoid them for complex logic, as it can reduce code readability.

Anonymous Classes vs Lambda Expressions

In Java 8 and later, lambda expressions are preferred over anonymous classes for functional interfaces (interfaces with only one abstract method), as they make the code more concise and readable. For interfaces with multiple methods, anonymous classes are still required.

How Do They Relate?

  • Lambda expressions can only be used with functional interfaces.

  • The lambda provides the implementation of the single abstract method in the functional interface.

 

Real-Time Project Example

Let’s consider a real-world scenario: Processing a List of Employees

Project Context:

  • You are working on an HR management system.

  • You need to filter employees based on different criteria like age, department, or salary.

Without Lambda Expressions (Verbose Approach)

 

java

Copy code

import java.util.ArrayList;
import java.util.List;

public class EmployeeFilter {
public static void main(String[] args) {
    List<Employee> employees = List.of(
        new Employee("Alice", 30, "IT", 60000),
        new Employee("Bob", 45, "HR", 50000),
        new Employee("Charlie", 25, "IT", 70000)
    );

// Filter employees aged above 30
    List<Employee> filtered = filterEmployees(employees, new EmployeeCriteria() {
        @Override
        public boolean test(Employee e) {
            return e.getAge() > 30;
        }
    });

System.out.println(filtered);
}

// Method to filter employees
public static List<Employee> filterEmployees(List<Employee> employees, EmployeeCriteria criteria) {
    List<Employee> result = new ArrayList<>();
    for (Employee e : employees) {
        if (criteria.test(e)) {
            result.add(e);
        }
    }
    return result;
}
}

// Functional Interface
interface EmployeeCriteria {
boolean test(Employee e);
}

// Employee Class
class Employee {
private String name;
private int age;
private String department;
private double salary;

public Employee(String name, int age, String department, double salary) {
    this.name = name;
    this.age = age;
    this.department = department;
    this.salary = salary;
}

public int getAge() {
    return age;
}

@Override
public String toString() {
    return name + " (" + age + ")";
}
}

 

With Lambda Expressions (Simpler Approach)

 

java

Copy code

import java.util.ArrayList;
import java.util.List;

public class EmployeeFilter {
public static void main(String[] args) {
    List<Employee> employees = List.of(
        new Employee("Alice", 30, "IT", 60000),
        new Employee("Bob", 45, "HR", 50000),
        new Employee("Charlie", 25, "IT", 70000)
    );

// Filter employees aged above 30 using lambda
    List<Employee> filtered = filterEmployees(employees, e -> e.getAge() > 30);

System.out.println(filtered);
}

// Method to filter employees
public static List<Employee> filterEmployees(List<Employee> employees, EmployeeCriteria criteria) {
    List<Employee> result = new ArrayList<>();
    for (Employee e : employees) {
        if (criteria.test(e)) {
            result.add(e);
        }
    }
    return result;
}
}

// Functional Interface
interface EmployeeCriteria {
boolean test(Employee e);
}

// Employee Class
class Employee {
private String name;
private int age;
private String department;
private double salary;

public Employee(String name, int age, String department, double salary) {
    this.name = name;
    this.age = age;
    this.department = department;
    this.salary = salary;
}

public int getAge() {
    return age;
}

@Override
public String toString() {
    return name + " (" + age + ")";
}
}

 

Explanation of Code

  1. Functional Interface:
    The EmployeeCriteria interface is a functional interface because it has only one abstract method, test(Employee e).

  2. Lambda Expression:
    Instead of creating an anonymous class to implement the test method, we use a lambda expression: e -> e.getAge() > 30.

  3. Concise and Readable:

    • The lambda expression makes the code concise and more readable.

    • You can change the filter criteria easily by just modifying the lambda, e.g., e -> e.getSalary() > 50000.

 

Summary

  • A functional interface provides the "blueprint" for a lambda expression.

  • A lambda expression is the implementation of the functional interface’s single abstract method.

  • Together, they simplify tasks like filtering, sorting, or transforming data in a functional programming style.

This combination is powerful in real-time projects where readability, maintainability, and concise coding are crucial.



Post a Comment

0 Comments