An interface that contains exactly one abstract method is known as a functional interface. It can have any number of default and static methods but can contain only one abstract method. It can also declare methods of the Object class.
Functional Interface is also known as Single Abstract Method Interfaces or SAM Interfaces. It is a new feature in Java that helps to achieve a functional programming approach.
The introduction of functional interfaces in Java 8 was a game changer, giving developers a more concise and transparent way of writing code. These interfaces paved the way for the adoption of design strategies that work within the Java ecosystem.
Let's understand functional interfaces with the help of examples.
In this example, a functional interface is created with one abstract method and implemented in a class.
Java
@FunctionalInterface
interface Sayable {
void say(String msg); // abstract method
}
public class Main implements Sayable {
public void say(String msg) {
System.out.println(msg);
}
// main method
public static void main(String[] args) {
Main fie = new Main(); // object created
fie.say("Hello there");
}
}
Output:
Hello there
Explanation
The annotation @FunctionalInterface just above the interface Sayable says that the interface Sayable is a functional interface and hence, it must contain only one abstract method. Violating this rule will lead to a compilation error.
In this example, the functional interface includes methods of the Object class along with one abstract method.
Java
@FunctionalInterface
interface Sayable {
void say(String msg); // only one abstract method
// It can contain any number of Object class methods.
int hashCode();
String toString();
boolean equals(Object obj);
}
public class Main implements interface Sayable { {
public void say(String msg) {
System.out.println(msg);
}
// main method
public static void main(String[] args) {
Main fie = new Main(); // object created
fie.say("Hello there");
}
}
Output:
Hello there
A functional interface must contain only one abstract method. If the interface contains more than one abstract method, then it will give a compilation error. The following program shows the same.
Java
interface Sayable {
void say(String msg); // abstract method
}
@FunctionalInterface
interface Doable extends Sayable {
// Invalid '@FunctionalInterface' annotation; Doable is not a functional interface
void doIt(); // an abstract method
// It has another abstract method, which is the say () method
// present in the interface Sayable
}
public class Main implements Doable {
public void say(String msg) {
System.out.println(msg);
}
public void doIt() {
System.out.println("In the doIt() method.");
}
// main method
public static void main(String[] args) {
Main fie = new Main(); // object created
fie.say("Hello there");
fie.doIt();
}
}
Output:
Compilation Error:
Compilation Error: Main.java:4: error: Unexpected @FunctionalInterface annotation
@FunctionalInterface
^
Doable is not a functional interface
multiple non-overriding abstract methods found in interface Doable
1 error
A functional interface can have any number of default methods, static methods, and Object class public methods (such as toString() or equals() method).
Java
interface Doable {
default void doIt() {
System.out.println("Do it now");
}
}
@FunctionalInterface
interface Sayable extends Doable {
void say(String msg); // only one abstract method
// It will also have the doIt() method, but it is a default method. Hence, it works.
}
public class Main implements Sayable {
public void say(String msg) {
System.out.println(msg);
}
public static void main(String[] args) {
Main fie = new Main(); // object created
fie.say("Hello there");
fie.doIt();
}
}
Output:
Hello there Do it now
Java 8 introduced several built-in functional interfaces in the java.util.function package, each designed to support common functional programming patterns. Some of the most commonly used functional interfaces include:
Supplier: Supplier is a built-in functional interface that only provides one output while accepting no arguments or inputs. In the java.util.function.Supplier<T> interface, the get() method is an abstract functional method. It is useful for generating and returning a value without accepting any arguments.
Syntax:
@FunctionalInterface public interface Consumer{ void accept(T t); }
@FunctionalInterface public interface Supplier{ T get(); }
Example
import java.util.function.Supplier;
public class Main {
// main method
public static void main(String[] args)
{
// Defining a Supplier that will take no arguments and will return a String
Supplier var = () -> "Good Morning from Supplier!";
// Executing the supplier with the help of the get() method
// This statement executes the lambda expression.
System.out.println(var.get());
}
}
Output:
Good Morning from Supplier!
Explanation
The statement () -> "Good Morning from Supplier!" depicts the supplier behavior. It says: "When invoked, return this text." Also, the lambda does not run the code immediately. The statement var.get() calls the method get() present in the Supplier interface. This method invocation actually executes the lambda expression.
Consumer: Consumer is another built-in functional interface in Java that has only one abstract method, which is accept(T t). Its return type is void. An operation that accepts a single input argument and returns no result.
Syntax:
@FunctionalInterface public interface Consumer{ void accept(T t); }
Example
import java.util.function.Consumer;
public class Main {
// main method
public static void main(String[] args) {
// Defining the Consumer for printing string
Consumer cnsmr = c -> System.out.println(c);
// Invoking the accept() method
cnsmr.accept("Good Morning, Consumer!");
// Defining a Consumer with a Method Reference
Consumer mthdRefPrinter = System.out::println;
mthdRefPrinter.accept("Good Morning Consumer method reference! ");
}
}
Output:
Good Morning, Consumer! Good Morning Consumer method reference!
Explanation
The first statement Consumer<String> cnsmr = c -> System.out.println(c); is a Consumer that processes a string. It has a lambda expression that takes the string c as the input and displays it on the console. The cnsmr.accept() invokes the accept method present in the Consumer functional interface, which executes the lambda expression. The second Consumer is similar to the first one, but it has a method reference. Note that mthdRefPrinter = System.out::println; is the replica of the lambda expression mthdRefPrinter = c -> System.out.println(c);
Predicate: Predicate is another built-in functional interface that has an abstract method called test(). The method accepts an argument of type T, and a Boolean value is returned. Predicate interface has other methods too, but they are either default methods or static methods.
Syntax:
@FunctionalInterface public interface Predicate{ boolean test(T t); }
Example
import java.util.function.Predicate;
import java.util.ArrayList;
public class PredicateExample {
public static void main(String[] args) {
// creating an array list
ArrayList al = new ArrayList();
// adding elements to the array list
al.add("India");
al.add("America");
al.add("Australia");
al.add("Russia");
al.add("Finland");
// Defining a predicate that processes a string.
// checks if the string has more than 5 characters or not
Predicate isLongerThan6 = s -> s.length() > 6;
for(String str : al) {
// invoking the test() method, which in turn executes the
// lambda expression written above
System.out.println("Does the string '" + str + "' have more than six characters? " + isLongerThan6.test(str));
}
}
}
Output:
Does the string 'India' have more than six characters? false Does the string 'America' have more than six characters? true Does the string 'Australia' have more than six characters? True Does the string 'Russia' have more than six characters? false Does the string 'Finland' have more than six characters? True
Function: In Java 8, Function is another functional interface; it takes an argument and returns an object. The return type and argument may or may not be the same. It has an abstract method apply(). Apart from the abstract method apply(), the Function interface has static and default methods too.
Syntax:
@FunctionalInterface public interface Function{ R apply(T t); }
Example
import java.util.function.Function;
import java.util.ArrayList;
public class Main {
// main method
public static void main(String[] args) {
// creating an array list
ArrayList al = new ArrayList();
// adding elements to the array list
al.add("India");
al.add("America");
al.add("Australia");
al.add("Russia");
al.add("Finland");
// Defining a Function that argument type is String, and return type is Integer
Function strLen = s -> s.length();
// Invoking the apply() method
for(String str : al) {
// invoking the apply() method, which in turn executes the
// lambda expression written above
System.out.println("The string '" + str + "' has " + strLen.apply(str) + " characters.");
}
}
}
Output:
The string 'India' has 5 characters. The string 'America' has 7 characters. The string 'Australia' has 9 characters. The string 'Russia' has 6 characters. The string 'Finland' has 7 characters.
UnaryOperator: UnaryOperator is a functional interface that extends the Function interface.
Syntax:
@FunctionalInterface public interface UnaryOperatorextends Function { }
Since UnaryOperator is a functional interface, it must have only one abstract method. That method comes from the Function interface. The UnaryOperator interface inherits the apply() method from the Function interface. Here, the return type of the apply() method is also T. Because of this inheritance, the syntax does not mention any explicit method inside the body of the interface.
Example
import java.util.function.UnaryOperator;
import java.util.Arrays;
import java.util.ArrayList;
import java.util.List;
public class Main {
// main method
public static void main(String[] args) {
List numberList = Arrays.asList(11, 12, 13, 14, 15, 16, 17, 18, 19, 20);
// Inovking the method mathFun().
List res = mathFun(numberList, y -> y * 3);
System.out.println("The result is: ");
for( int num : res)
System.out.print(num + " ");
}
// mathFun() is a static and generic method. It can handle data type of
// type T. It will return a list of type T. The method takes two arguments
// is the list and another is UnaryOperator.
public static List mathFun(List list, UnaryOperator unryOp) {
List res = new ArrayList<>();
for (T l : list) {
// invokes the method apply(), which excutes the lambda expresssion
// mentioned above.
res.add(unryOp.apply(l));
}
return res;
}
}
Output:
The result is: 33 36 39 42 45 48 51 54 57 60
BiFunction: BiFunction is another functional interface introduced in Java 8. It has two methods. One is an abstract functional method, apply() that executes the lambda expression, and another is the default method andThen().
Syntax:
@FunctionalInterface public interface BiFunction{ R apply(T t, U u); }
T is the type of the first argument, and U is the type of the second argument. R is the return type.
Example
import java.util.function.BiFunction;
public class Main {
// main method
public static void main(String[] args) {
// Here, BiFunction takes two integers and returns a String
// After multiplication of x and y, it gets converted into a string
// as we are concatenating the result with ""
BiFunction mul = (x, y) -> "" + (x * y);
int no1 = 15;
int no2 = 40;
// invoking the method apply(), which in turn executes
// the lambda expression
String res = mul.apply(no1, no2);
System.out.println("Multiplying numbers '" + no1 + "' and '" + no2 + "', result is: "+ res);
// BiFunction for adding two integers.
// Here, the first and the second argument both
// have type Integer, and the return type is also Integer
BiFunction sum = (a, b) -> a + b;
// Invoking the apply method to add two numbers by executing
// the lambda expression
int output = sum.apply(no1, no2);
System.out.println("Adding numbers '" + no1 + "' and '" + no2 + "', result is: "+ output);
}
}
Output:
Output: Multiplying numbers '15' and '40', result is: 600 Adding numbers '15' and '40', result is: 55
BinaryOperator: BinaryOperator is another built-in functional interface introduced in Java 8. This interface extends the BiFunction interface. Thus, the abstract fuctional method apply() present in the BiFunction interface gets copied to the BinaryOperator interface because of inhertance.
Syntax:
@FunctionalInterface public interface BiFunction{ }
T is the type of the first argument, T is the type of the second argument. T is also the return type.
Example
import java.util.function.BinaryOperator;
import java.util.List;
import java.util.Arrays;
public class Main {
public static void main(String[] args) {
// array of integers
Integer[] numArr = {11, 12, 13, 14, 15, 16, 17, 18, 19, 20};
// Invoking the method mathSum(). It has three arguments.
// The lambda expression adds the two integers x and y
Integer sum = mathSum(Arrays.asList(numArr), 0, (x, y) -> x + y);
System.out.print("For the numbers: ");
for( var k : numArr) {
System.out.print(k + " ");
}
System.out.println();
System.out.println("The total is: " + sum);
}
// mathSum() is a generic method. It has type T.
// Its first argument is a list of type T.
// The second argument is a value init of type T.
// The third argument is a BinaryOperator
// In this program, T is of type Integer
public static T mathSum(List list, T init, BinaryOperator bo) {
T res = init; //res is initialized with the value init
// looping through the list
for (T k : list) {
// invoking the method apply(), which in turn executes the
// lambda expression to add two numbers res and k
res = bo.apply(res, k);
}
return res;
}
}
Output:
For the numbers: 11 12 13 14 15 16 17 18 19 20 The total is: 155
These functional interfaces provide a foundation for writing functional-style code in Java. They encapsulate common patterns and enable developers to write more concise and readable code.
We can also define our own custom functional interface. The following is the list of functional interfaces that belong to java.util.function package
| Interface | Description |
|---|---|
| BiConsumer<T,U> | It represents an operation that accepts two input arguments and returns no result. |
| Consumer<T> | It represents an operation that accepts a single argument and returns no result. |
| Function<T,R> | It represents a function that accepts one argument and returns a result. |
| Predicate<T> | It represents a predicate (boolean-valued function) of one argument. |
| BiFunction<T,U,R> | It represents a function that accepts two arguments and returns a a result. |
| BinaryOperator<T> | It represents an operation upon two operands of the same data type. It returns a result of the same type as the operands. |
| BiPredicate<T,U> | It represents a predicate (boolean-valued function) of two arguments. |
| BooleanSupplier | It represents a supplier of boolean-valued results. |
| DoubleBinaryOperator | It represents an operation upon two double type operands and returns a double type value. |
| DoubleConsumer | It represents an operation that accepts a single double type argument and returns no result. |
| DoubleFunction<R> | It represents a function that accepts a double type argument and produces a result. |
| DoublePredicate | It represents a predicate (boolean-valued function) of one double type argument. |
| DoubleSupplier | It represents a supplier of double type results. |
| DoubleToIntFunction | It represents a function that accepts a double type argument and produces an int type result. |
| DoubleToLongFunction | It represents a function that accepts a double type argument and produces a long type result. |
| DoubleUnaryOperator | It represents an operation on a single double type operand that produces a double type result. |
| IntBinaryOperator | It represents an operation upon two int type operands and returns an int type result. |
| IntConsumer | It represents an operation that accepts a single integer argument and returns no result. |
| IntFunction<R> | It represents a function that accepts an integer argument and returns a result. |
| IntPredicate | It represents a predicate (boolean-valued function) of one integer argument. |
| IntSupplier | It represents a supplier of integer type. |
| IntToDoubleFunction | It represents a function that accepts an integer argument and returns a double. |
| IntToLongFunction | It represents a function that accepts an integer argument and returns a long. |
| IntUnaryOperator | It represents an operation on a single integer operand that produces an integer result. |
| LongBinaryOperator | It represents an operation upon two long type operands and returns a long type result. |
| LongConsumer | It represents an operation that accepts a single long type argument and returns no result. |
| LongFunction<R> | It represents a function that accepts a long type argument and returns a result. |
| LongPredicate | It represents a predicate (boolean-valued function) of one long type argument. |
| LongSupplier | It represents a supplier of long type results. |
| LongToDoubleFunction | It represents a function that accepts a long type argument and returns a result of double type. |
| LongToIntFunction | It represents a function that accepts a long type argument and returns an integer result. |
| LongUnaryOperator | It represents an operation on a single long type operand that returns a long type result. |
| ObjDoubleConsumer<T> | It represents an operation that accepts an object and a double argument, and returns no result. |
| ObjIntConsumer<T> | It represents an operation that accepts an object and an integer argument. It does not return result. |
| ObjLongConsumer<T> | It represents an operation that accepts an object and a long argument, it returns no result. |
| Supplier<T> | It represents a supplier of results. |
| ToDoubleBiFunction<T,U> | It represents a function that accepts two arguments and produces a double type result. |
| ToDoubleFunction<T> | It represents a function that returns a double type result. |
| ToIntBiFunction<T,U> | It represents a function that accepts two arguments and returns an integer. |
| ToIntFunction<T> | It represents a function that returns an integer. |
| ToLongBiFunction<T,U> | It represents a function that accepts two arguments and returns a result of long type. |
| ToLongFunction<T> | It represents a function that returns a result of long type. |
| UnaryOperator<T> | It represents an operation on a single operand that returnsa a result of the same type as its operand. |
We request you to subscribe our newsletter for upcoming updates.