Functional Interface in Java

functional interface in Java

What is a Functional Interface in Java ?

  • A Functional Interface in java is an interface that containsonly one abstract method. You can create your own functional interface using@FunctionalInterfaceannotation as well.
  • It can have multiple default or static methods, but only one abstract method
  • Functional interfaces are mainly used to enable Lambda Expressions in Java.
  • @FunctionalInterface annotation is optional but recommended

Creating a Functional Interface in Java

We can create a functional interface by using @FunctionalInterface. However, it is optional to use if the interface only contains one abstract method, Functional Interface should follow SAM, which stands for Single Abstract Method principle.


//Example: Simple Functional Interface
@FunctionalInterface
interface MyFunctionalInterface {
    void display(); // Single abstract method
}

public class FunctionalInterfaceExample {
    public static void main(String[] args) {
        // Using Lambda Expression
        MyFunctionalInterface obj = () -> System.out.println("Hello from Lambda!");
        obj.display();
    }
}

Output:
Hello from Lambda!


@FunctionalInterface Annotation

The @FunctionalInterface annotation is not mandatory, but it is recommended because:
✅ It ensures the interface has only one abstract method.
✅ It prevents accidental addition of extra abstract methods.


//Example: Using @FunctionalInterface
@FunctionalInterface
interface Calculator {
    int add(int a, int b);
}

// This will work fine
public class TestFunctional {
    public static void main(String[] args) {
        Calculator sum = (a, b) -> a + b;
        System.out.println("Sum: " + sum.add(5, 10));
    }
}
Output:
Sum: 15


Default and Static Methods in Functional Interfaces

A functional interface can have multiple default and static methods but only one abstract method.

  • Default methods can be overridden
  • Static methods are called with the interface name

Example: Default and Static Methods in Functional Interface
@FunctionalInterface
interface MyInterface {
    void show(); // Abstract method

    // Default method
    default void defaultMethod() {
        System.out.println("This is a default method");
    }

    // Static method
    static void staticMethod() {
        System.out.println("This is a static method");
    }
}

public class DefaultStaticExample {
    public static void main(String[] args) {
        MyInterface obj = () -> System.out.println("Lambda for show() method");
        obj.show();
        obj.defaultMethod(); // Calling default method
        MyInterface.staticMethod(); // Calling static method
    }
}
Output:
Lambda for show() method
This is a default method
This is a static method

What are Java's Built-in Functional Interfaces (from java.util.function)

Java provides several built-in functional interfaces in the java.util.function package. Predicate, Consumer, Supplier, Function, Bi-Function are the built in functional interface in Java.

Interface NameAbstract MethodDescription
Predicate<T>boolean test(T t)Evaluates a condition
Function<T, R>R apply(T t)takes a input of one type and then return a result in another type
Consumer<T>void accept(T t)accept a parameter but does not return anything
Supplier<T>T get()Returns a value without input
BiFunction<T, U, R>R apply(T t, U u)Takes two arguments and returns one

Predicate Functional Interface

  • We can use the functional interface predicate whenever we want to evaluate something.
  • It has a test method that returns true or false based on the condition.

Example : filter() method in stream uses predicate functional interface. It expects a predicate as input.


//Predicate<T> – Checks a Condition
import java.util.function.Predicate;

import java.util.List;
import java.util.Arrays;
import java.util.stream.Collectors;
import java.util.function.Predicate;

public class PredicateExample {
    public static void main(String[] args) {
        // Sample list of integers
        List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
        
        // Predicate to check if a number is even
        Predicate<Integer> isEven = num -> num % 2 == 0;
        System.out.println(isEven.test(20));

        // Using filter with Predicate to get even numbers
        List<Integer> evenNumbers = numbers.stream()
                                           .filter(isEven)  // Predicate applied here
                                           .collect(Collectors.toList());

        System.out.println(evenNumbers);  
    }
}
Output: 
true
[2, 4, 6, 8, 10]

Function Functional Inteface

  • The Function<T, R> functional interface is used to represent a function that accepts one argument of type T and returns a result of type R.
  • It is commonly used for operations like transforming or mapping data in functional programming. It has an apply() method.

Example : map method in stream API uses Function. Internally apply method is invoked.


//Using Function to Transform a List of Objects
import java.util.List;
import java.util.stream.Collectors;
import java.util.function.Function;

public class FunctionListExample {
    public static void main(String[] args) {
        // List of integers
        List<Integer> numbers = List.of(1, 2, 3, 4, 5);

        // Function to square a number
        Function<Integer, Integer> squareFunction = num -> num * num;

        // Using map with the Function to transform the list
        List<Integer> squaredNumbers = numbers.stream()
                                              .map(squareFunction)  // Applying the function to each element 
                                              //or we can use map(num->num*num)
                                              .collect(Collectors.toList());

        System.out.println(squaredNumbers);  // Output: [1, 4, 9, 16, 25]
        //simple ussage of predicate
        Function<Integer, Integer> multiplyByTwo = num -> num * 2;

        // Applying the function
        System.out.println(multiplyByTwo.apply(5));   // Output: 10
        System.out.println(multiplyByTwo.apply(100)); // Output: 200
        //apply(): The apply() method is used to apply the function to the given argument (in this case, multiplying by 2).
    }
}


Consumer Functional Interface

  • The Consumer<T> functional interface is part of the java.util.function package and represents an operation that takes a single input argument and returns no result.
  • It’s typically used when you need to perform an action on an object (like printing, modifying, or updating an object), but don’t want to return any value.
  • It has an accept() method.
  • forEach() method of a list is an example of using Consumer functional interface

//Using Consumer to Print Items in a List
import java.util.List;
import java.util.ArrayList;
import java.util.function.Consumer;

public class ConsumerExample {
    public static void main(String[] args) {
        // List of integers
        List<Integer> numbers = List.of(1, 2, 3, 4, 5);

        // Consumer to print each number
        Consumer<Integer> printConsumer = number -> System.out.println("Number: " + number);

        // Applying the Consumer to each element of the list
        numbers.forEach(printConsumer);  // Output: Number: 1, Number: 2, ..., Number: 5
    }
}
//The Consumer interface here takes an Integer and simply prints it to the console.
//The forEach() method of the List is used to apply the Consumer to each element

Supplier Functional Interface

The Supplier<T> functional interface is useful when you need to generate or supply a value without any input. It’s often used in scenarios like lazy initialization, providing default values, or when generating values for streams or other data structures

  • The Supplier interface provides a result without any input.
  • Supplier<T> has a single abstract method:
  • T get(): It supplies a result of type T.
  • Example of Supplier is orElseGet() method.

import java.util.function.Supplier;

public class SupplierOptionalExample {
    public static void main(String[] args) {
    
        // Supplier to provide a default value 
        Supplier<String> defaultNameSupplier = () -> "Default Name";
        System.out.println(defaultNameSupplier.get());
        //output Default Name
    }
}


A Supplier<String> is created with the lambda expression () -> “Default Name”. This Supplier will provide the string “Default Name” when its get() method is called.

BiFunction Functional Interface

  • The BiFunction<T, U, R> functional interface represents a function that accepts two arguments of types T and U, and returns a result of type R.
  • It is part of the java.util.function package in Java 8 and later.
  • The BiFunction interface is commonly used in scenarios where you need to process or combine two inputs to produce a result.
  • It has apply method : R apply(T t, U u);
Basic Usage of BiFunction Functional Interface

import java.util.function.BiFunction;

public class BiFunctionExample {
    public static void main(String[] args) {
         // Example 1: Basic BiFunction - Adding two integers
        System.out.println("Example 1: Adding two integers");
        BiFunction<Integer, Integer, Integer> add = (a, b) -> a + b;
        System.out.println("Addition of 10 and 20: " + add.apply(10, 20));  // Output: 30
        System.out.println();
        
        // Example 2: BiFunction - Concatenating two strings
        System.out.println("Example 2: Concatenating two strings");
        BiFunction<String, String, String> concatenate = (str1, str2) -> str1 + str2;
        System.out.println("Concatenation of 'Hello, ' and 'World!': " + concatenate.apply("Hello, ", "World!"));  
        // Output: Hello, World!
        System.out.println();
        
        // Example 3: BiFunction - Performing arithmetic operations (Addition and Multiplication)
        System.out.println("Example 3: Performing arithmetic operations");
        BiFunction<Integer, Integer, Integer> addNumbers = (a, b) -> a + b;
        BiFunction<Integer, Integer, Integer> multiplyNumbers = (a, b) -> a * b;

        // Applying the BiFunctions
        System.out.println("Addition of 5 and 3: " + addNumbers.apply(5, 3));  // Output: 8
        System.out.println("Multiplication of 5 and 3: " + multiplyNumbers.apply(5, 3));  // Output: 15
        System.out.println();
    }
}
Output:
Example 1: Adding two integers
Addition of 10 and 20: 30

Example 2: Concatenating two strings
Concatenation of 'Hello, ' and 'World!': Hello, World!

Example 3: Performing arithmetic operations
Addition of 5 and 3: 8
Multiplication of 5 and 3: 15

Functional Interface with Streams


//Example: Filtering List using Predicate
import java.util.*;
import java.util.function.Predicate;
import java.util.stream.Collectors;

public class FunctionalWithStreams {
    public static void main(String[] args) {
        List<String> names = Arrays.asList("Alice", "Bob", "Charlie", "Anna");

        // Filter names starting with 'A'
        Predicate<String> startsWithA = name -> name.startsWith("A");
        List<String> filteredNames = names.stream()
                                          .filter(startsWithA)
                                          .collect(Collectors.toList());

        System.out.println(filteredNames); // [Alice, Anna]
    }
}
✔ Functional Interface + Lambda + Streams = Clean Code

Custom Functional Interface with Generics

We can create a generic, custom functional interface for reusability, In which we can support multiple data types using generics.


//Example: Generic Functional Interface
@FunctionalInterface
interface GenericFunction<T> {
    T operate(T a, T b);
}

public class GenericFunctionalInterfaceExample {
    public static void main(String[] args) {
        GenericFunction<Integer> add = (a, b) -> a + b;
        GenericFunction<String> concatenate = (a, b) -> a + b;

        System.out.println("Addition: " + add.operate(5, 10)); // 15
        System.out.println("Concatenation: " + concatenate.operate("Hello", " World")); // Hello World
    }
}

Functional Interface default Method

The primary benefit of functional interface default methods is that they allow you to add new methods to an interface without breaking the existing implementation of the interface.

Default methods allow you to add new methods to an interface without affecting the classes that implement the interface. Without default methods, adding a new method to an interface would break the existing classes implementing it, as they would be required to provide implementations for the new methods.

Functional Interfaces in Java before Java 8

Before Java 8, Java did not have functional interfaces. Functional interfaces guarantee that only one abstract method is defined within the interface.

Prior to Java 8, we had to implement these interfaces using concrete classes or anonymous inner classes. With the introduction of functional interfaces in Java 8, users can leverage functional programming concepts, including the use of lambda expressions.

Functional Interface vs Interface

There are several Key Differences between normal Interfaces and Java 8+ Functional Interfaces. Here is the table that shows functional interface vs interface feature-wise.

FeatureBefore Java 8Java 8 and Beyond
PurposeUsed for creating abstractions for classes to implementUsed for functional programming and enabling lambda expressions
Abstract MethodsMultiple abstract methods allowedTypically one abstract method (single abstract method interface)
Default MethodsNot allowedAllowed using the default keyword
Static MethodsNot allowed in interfacesAllowed using the static keyword
Lambda ExpressionsNot supportedSupported, allowing a compact way to implement functional interfaces
@FunctionalInterfaceNot used or supportedCan be used to explicitly indicate a functional interface
Example UsageImplemented through classesUsed primarily with lambdas and method references

Functional Interface vs abstract Class

  • Functional Interfaces are highly suited for scenarios requiring single behavior or operations that can be expressed as functions, especially in functional programming style (e.g., lambda expressions).

  • Abstract Classes are useful when there is a need for common functionality and shared state across a family of classes, where you can leverage inheritance and constructors.

Each serves a different purpose, so choosing between them depends on your design needs. Below is the table that compares functional interface and abstract class.

Functional Interface vs abstract Class

FeatureFunctional InterfaceAbstract Class
PurposeRepresents a single behavior or function that can be implemented using lambda expressions or method references.Provides a foundation for other classes to extend, enabling shared functionality and code reuse.
Number of Abstract MethodsMust contain exactly one abstract method (Single Abstract Method or SAM).Can contain multiple abstract methods that subclasses must implement.
Lambda Expression SupportSupported: Functional interfaces can be implemented using lambda expressions or method references.Not Supported: Abstract classes cannot be instantiated with lambda expressions because they may have multiple abstract methods and require subclassing.
MethodsCan have default methods (methods with an implementation) and static methods, in addition to the single abstract method.Can have both abstract methods (without implementation) and concrete methods (with implementation).
StateStateless: Functional interfaces cannot have instance variables (no state). They are only concerned with behavior.Stateful: Abstract classes can have instance variables to store object state, providing a way to maintain state across subclasses.
ConstructorsCannot have constructors because they do not maintain state.Can have constructors to initialize object state in the abstract class or its subclasses.
InheritanceCan extend other interfaces, but it can only have one abstract method.Can extend other classes (abstract or concrete) and implement multiple interfaces, allowing more complex inheritance hierarchies.
InstantiationCannot be instantiated directly. They are intended to be implemented using lambda expressions or method references.Cannot be instantiated directly, but can be instantiated indirectly through concrete subclasses.
Use CaseIdeal for functional programming tasks such as handling callbacks, event handling, or operations involving lambda expressions and streams.Best suited for creating a common base class that provides shared functionality and state across related classes.

Functional Interface use in Java

  • A functional interfaceis an interface that has exactly one abstract method. It can have multiple default or static methods, but it is primarily designed to have a single behavior that can be implemented, often using lambda expressions or method references
  • It Enables functional programming in Java using lambda expressions & streams
  • Functional interfaces are used extensively in the Streams API to perform operations like map, filter, and reduce.
  • Functional interfaces can be used for asynchronous programming, where tasks are executed asynchronously. For example, Java’s CompletableFuture can work with functional interfaces for callback operations.

Functional Interface Advantages

  • Simplifies Code: Functional interfaces allow you to use lambda expressions, making the code more concise and readable by eliminating boilerplate code (like anonymous classes).

  • Promotes Functional Programming: They enable functional programming in Java, allowing you to treat behavior as parameters and pass functions around.

  • Enhances Code Reusability: Functional interfaces make it easy to reuse functions and pass them as arguments, improving modularity.

  • Enables Method References: They allow you to use method references, which provide a shorthand for lambda expressions, enhancing readability.

  • Better Integration with Streams: Functional interfaces are integral to the Streams API, enabling powerful and expressive operations like filtering, mapping, and reducing collections.

  • Support for Parallelism: Functional interfaces, in conjunction with Streams, support parallel execution, improving performance for large data sets.

  • Encourages Cleaner, More Readable Code: By focusing on single method behavior, they promote cleaner and more understandable code compared to traditional interfaces or classes.

Conclusion on Functional Interfaces

  • Functional Interfaces enables Lambda Expressions
  • Can have default and static methods
  • Java provides built-in functional interfaces
  • Works well with Streams and Collections
  • Reduces boilerplate code

Functional Interface Interview Questions

Top 20 functional interface interview questions

There so many tricky and important functional interface interview questions asked in the interview for freshers as well as experienced. Here are 20 interview questions, including some tricky questions for experienced Java professionals, along with their answers and coding examples.

What is a functional interface in java ?

Answer: A functional interface in Java is an interface with exactly one abstract method. It can have multiple default and static methods. Functional interfaces are used to represent single functionality or behavior, and they can be implemented using lambda expressions or method references.


Can you give an example of a built-in functional interface in java ?

Answer: One of the common built-in functional interfaces is Predicate<T>. It represents a condition, taking an argument and returning a boolean result.

Predicate<Integer> isEven = num -> num % 2 == 0;

System.out.println(isEven.test(4)); // true


What does @FunctionalInterface annotation do?

Answer: The @FunctionalInterface annotation is used to indicate that an interface is intended to be a functional interface. It is optional but helps to ensure that the interface adheres to the single abstract method rule.


Can a functional interface have more than one abstract method?

Answer: No, a functional interface can have only one abstract method. If it has more than one abstract method, it will not be considered a valid functional interface.


Can a functional interface have default methods?

Answer: Yes, a functional interface can have default methods. Default methods have a body and can provide a default implementation, allowing flexibility while maintaining the contract of a single abstract method.


Can a functional interface have static methods?

Answer: Yes, a functional interface can have static methods. These methods belong to the interface itself and cannot be overridden by implementing classes.


Can we use lambda expressions with a functional interface?

Answer: Yes, lambda expressions are specifically used to provide the implementation of the single abstract method of a functional interface.


What is the difference between a functional interface and an abstract class?

  • A functional interface can only have one abstract method, while an abstract class can have multiple abstract methods.

  • A functional interface is meant for functional programming and often used with lambda expressions, while an abstract class is typically used for class hierarchies with shared behavior.


Can functional interfaces extend other interfaces?

Answer: Yes, a functional interface can extend another functional interface (with exactly one abstract method). It cannot extend interfaces that have more than one abstract method.


What are some examples of built-in functional interfaces in java.util.function package?

Answer:

  • Predicate<T>: Represents a condition.

  • Function<T, R>: Transforms one type to another.

  • Consumer<T>: Performs an action without returning a result.

  • Supplier<T>: Provides a result without input.

  • BiFunction<T, U, R>: Takes two arguments and returns a result.


What is a Function interface ?

Answer: Function<T, R> takes an argument of type T and returns a result of type R.

Function<Integer, String> intToString = i -> “Number: “ + i;

System.out.println(intToString.apply(5)); // Output: Number: 5


What is a Predicate interface ?

Answer: Predicate<T> takes a single argument and returns a boolean.

Predicate<Integer> isPositive = num -> num > 0;

System.out.println(isPositive.test(5)); // Output: true


Can a functional interface have constructors?

Answer: No, a functional interface cannot have constructors. It is only used for behavior implementation and doesn’t deal with object instantiation.


What is Consumer Interface ?

Answer: Consumer<T> performs an action on a given input without returning a result.

Consumer<String> print = str -> System.out.println(str);

print.accept(“Hello, World!”); // Output: Hello, World!


What is a Supplier interface?

Answer: Supplier<T> provides a result of type T without taking any input.

Supplier<Double> randomValue = () -> Math.random();

System.out.println(randomValue.get()); // Output: random value


Can functional interfaces have multiple abstract methods if they are inherited from other interfaces?

Answer: No, if a functional interface inherits another interface that has more than one abstract method, it will no longer be considered a valid functional interface. A functional interface must have only one abstract method.

What is a BiFunction Interface ?

Answer: BiFunction<T,U,R> takes two arguments and returns a result.

BiFunction<Integer, Integer, Integer> add = (a, b) -> a + b;

System.out.println(add.apply(3, 4)); // Output: 7


Can we use method references with functional interfaces?

Answer: Yes, method references are often used with functional interfaces as a shorthand for lambda expressions.

public class MethodReferenceExample {

public static void printMessage(String message) {

System.out.println(message);

}

public static void main(String[] args) {
Consumer<String> printer = MethodReferenceExample::printMessage;
printer.accept(“Hello!”); // Output: Hello!
}
}


What will happen if you try to add a second abstract method to a functional interface?

Answer: If a second abstract method is added to a functional interface, it will no longer be considered a functional interface and will not compile because it violates the rule of having exactly one abstract method.


What is a tricky use case of functional interfaces?

Answer: One tricky case is when you have an abstract class implementing a functional interface. This can sometimes lead to unexpected results because the abstract class can have concrete methods and state.

Below example shows that while the abstract class implements the interface, it does not affect the lambda’s behavior when the abstract methods are implemented using lambdas.


@FunctionalInterface
interface MyFunction {
void apply();
}

abstract class MyAbstractClass implements MyFunction {
void extraMethod() {
System.out.println("Extra method");
}
}

public class TrickyExample {
public static void main(String[] args) {
MyFunction func = () -> System.out.println("Functional Interface");
func.apply(); // Output: Functional Interface
}
}