The article aims to describe functional interface in Java 8 with the help of hello world code samples.
What is Functional Interface?
Simply speaking, a functional interface is the plain old Java interface with JUST ONE and ONLY ONE abstract method also termed as the functional method. However, that does not stop functional interface to not contain default and static methods. Following is an example of a functional interface:
@FunctionalInterface
public interface HelloInterface {
// Abstract method
void sayHello( String name );
// Static method
static void sayThanks() {
System.out.println( "Thank You!" );
}
// Default method
default void sayHelloWorld() {
System.out.println( "Hello World!" );
}
}
Observations:
- In the above example, you would see the interface, HelloInterface, consisting of an abstract method (sayHello), a static method (sayThanks) and a default method (sayHelloWorld).
- There is an annotation, @FunctionalInterface, used to identify the interface as a functional interface. This annotation is not a mandatory requirement for the compiler to recognize an interface as a functional interface, but merely an aid to capture design intent and enlist the help of the compiler in identifying accidental violations of design intent.
How could instance of Functional Interface be created?
The instances of functional interfaces can be created with lambda expressions, method references, or constructor references. Look at the following code samples. In the following code, HelloInterface is a functional interface whose code is shown above.
public class HelloFunctionalInterface {
//Method taking functional interface as a parameter
public void print(HelloInterface hi, String name) {
hi.sayHello(name);
}
public void singASong( String song ) {
System.out.println( song );
}
public static void sayHello( String name ) {
System.out.println("Method: Hello, " + name );
}
public static void main(String[] args) {
String name = "Vitalflux";
HelloFunctionalInterface hfi = new HelloFunctionalInterface();
//
// Passing Traditional Anonymous Class for Functional Interface
//
hfi.print(new HelloInterface() {
public void sayHello(String name) {
System.out.println("Anonymous: Hello, " + name);
}
}, name);
//
// Passing Lambda Expression for Functional Interface
//
hfi.print( aName -> {
System.out.println("Lambda: Hello, " + aName);
}, name);
//
// Passing Method Reference for Functional Interface
//
hfi.print(HelloFunctionalInterface::sayHello, name); // Reference to a static method of Containing class
hfi.print(hfi::singASong, name); //Reference to an instance method of Containing object
HelloUtil hu = new HelloUtil( name );
hfi.print(hu::sayHello, name); // Reference to an instance method of an arbitrary object of a particular type
//
// Passing Constructor Reference for Functional Interface
//
hfi.print(HelloUtil::new, name); // Reference to a constructor
}
}
The above code sample represents following that could be passed as a parameter for functional interface:
- Tranditional anonymous class
- Lambda expression
- Method reference
- Constructor reference
Functional Interface & Significance of Package java.util.function
Package java.util.function consists of general purpose functional interfaces that could be used by user code in different scenarios. Before going into the details of the package, lets take a look at an example below:
Following is a functional interface, CheckPerson, which has an abstract method “test” that takes one parameter and returns a boolean value.
interface CheckPerson {
boolean test(Person p);
}
The above method is pretty simple. There could be a need for several similar functional interfaces with a method like above which could take an argument and return a boolean value. Defining such interfaces with above method may be cumbersome as all of these interfaces mean same. Java 8 came up with an answer to this issue by defining an out-of-box functional interface termed as “Predicate”. Predicate<T> is a functional interface in java.util.function which has an abstract method “test” which takes an argument and returns a boolean. T is an object type.
Thus, in place of CheckPerson custom functional interface, one could use Predicate<Person> and achieve the same objective. This does avoid functional interface sprawl to a great extent.
Java.util.function defines following four basic function types:
- Function<T, R>: Used for functional interfaces where the abstract method accepts one argument and produces a result. The functional method is apply(object).
- Consumer<T>: Used for functional interfaces where the abstract method is an operation that accepts a single input argument and returns no result. The functional method is accept(object).
- Predicate<T>: Used for functional interfaces where the abstract method accepts an object and returns boolean. The functional method is test(object).
- Supplier<T>: Used for functional interfaces where the abstract method is get(), supplier of results.
Then, there are functional interfaces similar to above which are prefixed with “Bi” to represent two entries/inputs that they take as method argument. Based on this, following are three other functional interfaces:
- BiFunction<T, U, R>: Takes two inputs and produces one output. The functional method is apply(object, object).
- BiConsumer<T,U>: Takes two inputs and produces no output. The functional method is accept(object, object).
- BiPredicate<T, U>: Takes two inputs and produces boolean result. The functional method is test(object, object).
The code samples below represents usage of BiFunction to apply addition operation to calculate the output.
public class HelloCalculatorUsingBiFunction {
public Long process( long num1, long num2, BiFunction biFunc ) {
return (Long) biFunc.apply( num1, num2);
}
public static void main(String[] args) {
HelloCalculatorUsingBiFunction hlbf = new HelloCalculatorUsingBiFunction();
BinaryOperator add = (x, y) -> x + y;
System.out.println( "Addition: " + hlbf.process( 4, 5, add ));
}
}
In addition to above, there are functional interfaces which represents the return types. Following are some of the examples:
- ToIntFunction<T>, ToIntBiFunction<T, U>: Produces an int-valued result
- ToLongFunction<T>, ToLongBiFunction<T, U>: Produces a long-valued result
[adsenseyu1]
- Agentic Reasoning Design Patterns in AI: Examples - October 18, 2024
- LLMs for Adaptive Learning & Personalized Education - October 8, 2024
- Sparse Mixture of Experts (MoE) Models: Examples - October 6, 2024
What is the code for HelloUtil in above example?
HelloUtil represents an arbitrary object with code such as following:
public class HelloUtil {
public HelloUtil(String name) {
System.out.println(“Constructor: Hello, ” + name);
}
public void sayHello( String name ) {
System.out.println(“Method: Hello, ” + name );
}
}