Back to Functional Interfaces
Q12Question 12 of 13

Write a custom @FunctionalInterface with boolean test(Employee e, double threshold) and use it as a lambda

Key Concepts
  • @FunctionalInterface enforces exactly one abstract method; a second causes a compile error
  • A custom interface with boolean test(Employee, double) is a domain-specific two-argument predicate
  • A named interface like SalaryCheck is clearer and avoids autoboxing vs BiPredicate<Employee, Double>
  • The annotation is optional for lambda support but prevents accidental contract-breaking later
  • Create custom interfaces when they improve readability, avoid boxing, or need a domain-specific name
  • Otherwise reuse Predicate, Function, and the standard set to keep the codebase conventional

Definition

A @FunctionalInterface with exactly one abstract method can be implemented by a lambda: define your own when the built-ins do not fit.
  • @FunctionalInterface tells the compiler to enforce exactly one abstract method.
  • A custom boolean test(Employee e, double threshold) is a domain-specific two-argument predicate.
  • Named interfaces like SalaryCheck are clearer than generic BiPredicate.
  • The annotation is a safety net: optional for lambdas but prevents accidental contract-breaking.
  • Create custom interfaces for readability, primitive avoidance, or domain naming.

Priya at the National Bank of India invents her own yes-or-no form that takes an employee and a threshold amount. Because the form has exactly one question to answer, she can fill it with a one-line lambda.

Understanding the Concepts

How do you write a custom functional interface?

Declare an interface with one abstract method and annotate it.

JAVA
package com.bank.hr;

@FunctionalInterface
public interface SalaryCheck {
    boolean test(Employee e, double threshold);
}
JAVA
package com.bank.hr;

import java.util.*;
import java.util.stream.Collectors;

public class CustomFIDemo {
    public static void main(String[] args) {
        SalaryCheck earnsMoreThan = (e, t) -> e.getSalaryAsDouble() > t;

        List<Employee> employees = List.of(
            new Employee("Rajesh", "Loans", 75000.0),
            new Employee("Priya",  "IT",   82000.0),
            new Employee("Amit",   "IT",   45000.0)
        );

        List<Employee> highEarners = employees.stream()
            .filter(e -> earnsMoreThan.test(e, 50_000))
            .collect(Collectors.toList());

        highEarners.forEach(e -> System.out.println(e.getName()));
    }
}
TEXT
Rajesh
Priya

Why not use BiPredicate?

| Feature | SalaryCheck | BiPredicate |
|---|---|---|
| Name | Domain-specific | Generic |
| Primitive boxing | None (primitive double) | Autoboxes double to Double |
| Readability | SalaryCheck.test(e, 50000) | BiPredicate.test(e, 50000.0) |

A named custom interface documents intent and avoids the boxing overhead of Double.

What does @FunctionalInterface enforce?

JAVA
// CORRECT: exactly one abstract method
@FunctionalInterface
interface SalaryCheck {
    boolean test(Employee e, double threshold);
}

// WRONG: two abstract methods → compile error
@FunctionalInterface
interface BrokenCheck {
    boolean test(Employee e, double threshold);
    String describe();  // second abstract method!
}

Default and static methods are fine; they do not count as abstract.

JAVA
@FunctionalInterface
interface SalaryCheck {
    boolean test(Employee e, double threshold);

    default SalaryCheck negate() {
        return (e, t) -> !this.test(e, t);
    }
}

When should you create a custom interface vs reuse built-ins?

| Create custom when | Reuse built-in when |
|---|---|
| The interface needs a domain-specific name | A standard name like Predicate suffices |
| Primitive parameters avoid boxing overhead | All parameters are objects |
| The interface needs default/static methods | No extra methods needed |

What are common mistakes?

Mistake: omitting the annotation and accidentally adding a second abstract method later.

JAVA
// Without @FunctionalInterface, someone could add a second method
// and break all existing lambdas. The annotation prevents this.

What should a senior engineer know?

  • @FunctionalInterface is a safety net, not a requirement. Lambdas work on any single-abstract-method interface.
  • Custom interfaces shine for domain DSLs: SalaryCheck, TransferValidator, LoanEligibility.
  • Keep the standard set (Predicate, Function, Consumer, Supplier) as the default choice.
  • Adding default methods to a custom functional interface provides composition (negate, and, or) without breaking the single-method contract.

What are common interview follow-up questions?

  • "Is @FunctionalInterface required for a lambda?" No, but it enforces the contract.
  • "Can a functional interface have default methods?" Yes, as many as needed.
  • "Can a functional interface extend another interface?" Yes, as long as the total has exactly one abstract method.

Visual Overview

DIAGRAM
  @FunctionalInterface
  SalaryCheck { boolean test(Employee, double) }
       │
       │  Lambda: (e, t) -> e.getSalary() > t
       ▼
  SalaryCheck earnsMoreThan = (e, t) -> salary > t;

  Usage:
  earnsMoreThan.test(Rajesh, 50000)  → 75000 > 50000 = true
  earnsMoreThan.test(Amit, 50000)    → 45000 > 50000 = false

Key Takeaway

A @FunctionalInterface with one abstract method can be implemented by a lambda. When the built-in interfaces do not fit your domain, declare a custom one like SalaryCheck with boolean test(Employee, double), which gives a domain-specific name and avoids autoboxing the primitive threshold.

The annotation tells the compiler to enforce the single-method contract, preventing someone from later adding a second abstract method and breaking all existing lambdas. At the National Bank of India, SalaryCheck was clearer than BiPredicate in the screening service.

Create custom functional interfaces when they improve readability, avoid boxing, or deserve a domain-specific name. Otherwise reuse Predicate, Function, and the standard set.

Remember one sentence: @FunctionalInterface enforces one abstract method and enables lambda implementation; create a custom one when the standard interfaces lack domain clarity or force boxing.

---

Share
Use ← → arrow keys to navigate