Java tutorials > Modern Java Features > Java 8 and Later > What are functional interfaces?
What are functional interfaces?
Functional interfaces are a cornerstone of functional programming in Java, introduced in Java 8. They enable you to treat functionality as a method argument, or code as data. A functional interface is essentially an interface with one and only one abstract method. This single abstract method (SAM) requirement allows functional interfaces to be seamlessly used with lambda expressions and method references.
Core Concept: The Single Abstract Method (SAM)
The defining characteristic of a functional interface is that it has exactly one abstract method. This is what allows the compiler to infer the target type when a lambda expression or method reference is used. An abstract method is one that's declared but not implemented in the interface itself. Default methods and static methods don't count against this rule; a functional interface can have any number of them. The @FunctionalInterface
annotation is optional but highly recommended. It's used to signal to the compiler that the interface is intended to be a functional interface. If you attempt to add a second abstract method, the compiler will throw an error.
A Simple Example: `MyInterface`
In this example, MyInterface
is a functional interface because it contains only one abstract method, myMethod
. We then create an instance of MyInterface
using a lambda expression that prints a message to the console. We also demonstrate using a method reference System.out::println
which satisfies the functional interface MyInterface
because println
takes a String argument, just like myMethod
. This is more compact than the Lambda expression.
java
@FunctionalInterface
interface MyInterface {
void myMethod(String message);
}
public class Main {
public static void main(String[] args) {
// Using a lambda expression
MyInterface myLambda = (String msg) -> System.out.println("Message: " + msg);
myLambda.myMethod("Hello, Functional Interface!");
// Using a method reference
MyInterface myMethodRef = System.out::println; //method reference
myMethodRef.myMethod("Hello from method reference!");
}
}
Common Functional Interfaces in `java.util.function`
The Using these interfaces reduces the need to define your own for simple cases.java.util.function
package provides numerous pre-defined functional interfaces that cover common use cases. Here are a few examples:
Predicate<T>
: Represents a predicate (boolean-valued function) of one argument.Consumer<T>
: Represents an operation that accepts a single input argument and returns no result.Function<T, R>
: Represents a function that accepts one argument and produces a result.Supplier<T>
: Represents a supplier of results.UnaryOperator<T>
: Represents an operation on a single operand that produces a result of the same type as its operand.BinaryOperator<T>
: Represents an operation upon two operands of the same type, producing a result of the same type as the operands.
Real-Life Use Case: Event Handling
This example shows how functional interfaces (specifically Consumer
) can be used for event handling. The EventManager
allows subscribers to register listeners (Consumer<Event>
) that will be notified when an event is published. This promotes loose coupling and makes the code more flexible and maintainable.
java
import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;
class Event {
private String name;
public Event(String name) { this.name = name; }
public String getName() { return name; }
}
class EventManager {
private List<Consumer<Event>> listeners = new ArrayList<>();
public void subscribe(Consumer<Event> listener) {
listeners.add(listener);
}
public void publish(Event event) {
listeners.forEach(listener -> listener.accept(event));
}
}
public class Main {
public static void main(String[] args) {
EventManager manager = new EventManager();
// Subscribe a listener using a lambda expression
manager.subscribe(event -> System.out.println("Event received: " + event.getName()));
// Publish an event
manager.publish(new Event("New user registered"));
}
}
Best Practices
@FunctionalInterface
to let the compiler help you maintain their contract.java.util.function
whenever possible before defining your own.
Interview Tip
Be prepared to explain what functional interfaces are, why they are used, and how they relate to lambda expressions and method references. Demonstrate your understanding of the core concept (SAM) and be able to provide examples of common functional interfaces.
When to Use Them
Use functional interfaces when you need to pass behavior as a method argument or when you want to represent a single action or operation. They are particularly useful with lambda expressions for concise and readable code.
Alternatives
Before Java 8, you would typically use anonymous inner classes to achieve similar functionality. However, functional interfaces and lambda expressions provide a more concise and readable alternative.
Pros
Cons
FAQ
-
Can a functional interface extend another interface?
Yes, a functional interface can extend another interface, but if the extended interface also has an abstract method, the functional interface must either implement that method or declare it abstract to still qualify as a functional interface (having one abstract method).
-
What happens if I don't use the `@FunctionalInterface` annotation?
The code will still compile and work as expected if the interface adheres to the rules of a functional interface (one abstract method). However, the annotation serves as documentation and allows the compiler to catch errors if you accidentally add a second abstract method.
-
Are default methods allowed in functional interfaces?
Yes, functional interfaces can have any number of default methods. Default methods provide a default implementation for a method in the interface, so they don't count towards the single abstract method rule.
-
Can I use functional interfaces with checked exceptions?
Yes, but you need to handle the checked exception within the lambda expression or method reference, or declare the abstract method to throw the exception. Alternatively, you can create a custom functional interface that handles the exception.