Java Functional Interface Explained with Code Samples

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:

  1. 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).
  2. 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]

Ajitesh Kumar

Ajitesh Kumar

I have been recently working in the area of Data analytics including Data Science and Machine Learning / Deep Learning. I am also passionate about different technologies including programming languages such as Java/JEE, Javascript, Python, R, Julia, etc, and technologies such as Blockchain, mobile computing, cloud-native technologies, application security, cloud computing platforms, big data, etc. I would love to connect with you on Linkedin. Check out my latest book titled as First Principles Thinking: Building winning products using first principles thinking.
Posted in Java. Tagged with .

2 Responses