Guide to Functional Interfaces in Java
This article will throw light on the latest Functional Interfaces introduced in Java 8. We will study what functional interfaces are and how we can use them to empower our application and make our code cleaner and faster.
We’ll run through about what Functional Interfaces are, how they couple with Lambda expressions to exhibit strong OOP behaviour and we will also study different Functional Interfaces introduced in detail with their methods. Let’s dive in now.
Functional Interfaces
Functional Interfaces focus on offering a single functionality through methods. Though these interfaces sounds new, every Java developer has used Functional Interfaces at least once during development. Some of the common interfaces are:
- java.lang.Runnable
- java.awt.event.ActionListener
- java.util.Comparator
- java.util.concurrent.Callable etc.
There is a common feature in all of the interfaces mentioned above—they all offer just a single method through their definition. These interfaces are also called Single Abstract Method interfaces (SAM Interfaces). A popular way in which these are used is by creating Anonymous Inner classes using these interfaces. The following code snippet describes an example:
public class AnonymousInnerClassTest {
public static void main(String[] args) {
new Thread(new Runnable() {
@Override
public void run() {
System.out.print("A thread created and running on DiscoverSDK");
}
}).start();
}
}
>>> A functional interface has exactly one abstract method.
In Java 8, a similar concept of SAM interfaces is recreated and is called Functional interfaces.
Lets try to have a look at a simple functional interface with only one abstract method:
@FunctionalInterface
public interface SimpleFuncInterface {
public void doWork();
}
The interface can also declare the abstract methods from the java.lang.Object class, but still the interface can be called as a Functional Interface:
@FunctionalInterface
public interface SimpleFuncInterface {
public void doWork();
public String toString();
public boolean equals(Object o);
}
>>> Instances of functional interfaces can be created with lambda expressions, method references, or constructor references.
It is also worth noticing that the compiler will treat any interface meeting the definition of a functional interface as a functional interface regardless of whether or not a Functional Interface annotation is present on the interface declaration.
Features of Functional Interfaces
Some observations about SAM interfaces are:
- A functional interface has only one abstract method but it can have multiple default methods.
- @FunctionalInterface annotation is used to ensure an interface can’t have more than one abstract method. The use of this annotation is optional.
- The java.util.function package contains many built-in functional interfaces in Java 8.
Pre-Built Functional Interfaces
There are a lot of re-usable functional requirements that can be captured by functional interfaces and lambdas. The designers of Java 8 have captured the common use cases and created a library of functions for them. A new package called java.util.function was created to host these common functions.
There are a handful of these interfaces added to the library, which we’ll discuss in the next section.
Predicates
Class : java.util.Predicate
Predicate function is used for checking a condition. They are boolean-valued functions of one argument. The interface contains various default methods for composing predicates to complex logical terms (and, or, negate). It accepts a single argument to evaluate to a boolean result. It has a single method test which returns the boolean value. See the interface sample below which is a generic type accepting any type T:
@FunctionalInterface
public interface Predicate<T> {
boolean test(T t);
}
From our knowledge of lambdas so far, we can deduce the lambda expression for this Predicate. Here are some examples of lambda expressions in code samples:
Predicate<String> predicate = (s) -> s.length() > 0;
predicate.test("DiscoverSDK"); // true
predicate.negate().test("DiscoverSDK"); // false
Predicate<Boolean> nonNull = Objects::nonNull;
Predicate<Boolean> isNull = Objects::isNull;
Predicate<String> isEmpty = String::isEmpty;
Predicate<String> isNotEmpty = isEmpty.negate();
Functions
Class : java.util.Function
A Function is a functional Interface whose primary and sole purpose in life is to return any result by working on a single input argument. It accepts an argument of type T and returns a result of type R, by applying specified logic on the input via the apply method. The interface definition is shown here:
// The T is the input argument while R is the return result
@FunctionalInterface
public interface Function<T, R> {
R apply(T t);
}
One of the examples is shown in following code snippet:
// convert centigrade to fahrenheit
Function<Integer,Double> centigradeToFahrenheitInt = x -> new Double((x*9/5)+32);
// String to an integer
Function<String, Integer> stringToInt = x -> Integer.valueOf(x);
// tests
System.out.println("C to F:" + centigradeToFahrenheitInt.apply(centigrade));
System.out.println(" String to Int: " + stringToInt.apply("4"));
Did you notice the two arguments to the Function: String and Integer? This indicates that the function is expecting a String and returning the Integer.
Suppliers
Suppliers produce a result of a given generic type. Unlike Functions, Suppliers don't accept arguments. It’s definition interface is given as:
@FunctionalInterface
public interface Supplier<T> {
T get();
}
Let’s look at one of the example code snippets now:
Supplier<Person> personSupplier = Person::new;
personSupplier.get(); // new Person
>>> Suppliers don't accept arguments.
There is no requirement that a new or distinct result be returned each time the supplier is invoked.
Consumers
Consumers represent operations to be performed on a single input argument. It’s interface definition is given as:
@FunctionalInterface
public interface Consumer<T> {
void accept(T t);
}
Let’s have a look at another example for this interface:
Consumer<Person> greeter = (p) -> System.out.println("Hello, " + p.firstName);
greeter.accept(new Person("Discover", "SDK"));
Comparators
Comparators are well known from older versions of Java. Java 8 adds various default methods to the interface.
Let’s look at an example:
Comparator<Person> comparator = (p1, p2) -> p1.firstName.compareTo(p2.firstName);
Person p1 = new Person("John", "Doe");
Person p2 = new Person("Java", "Wonderland");
comparator.compare(p1, p2); // > 0
comparator.reversed().compare(p1, p2); // < 0
Optionals
Optionals are not functional interfaces, instead it's a nifty utility to prevent NullPointerException. It's an important concept for the next section, so let's have a quick look at how Optionals work.
Optional is a simple container for a value which may be null or non-null. Think of a method which may return a non-null result but sometimes returns nothing. Instead of returning null you return an Optional in Java 8.
Optional<String> optional = Optional.of("foo");
optional.isPresent(); // true
optional.get(); // "bam"
optional.orElse("bar"); // "bam"
optional.ifPresent((s) -> System.out.println(s.charAt(0))); // "b"
Additionally, another question pops up. Can you override toString() and equals() method of a Functional Interface?
No, lambda expressions are used to express one method interfaces as if they are just functions. It's an element of functional languages that was implemented in Java (an OOP language).
To override toString, you must implement the interface in a class.
Interface with default methods vs Abstract class
You must think, if Functional Interfaces can contain default methods, what is the difference between Functional Interfaces and Abstract classes? Aren’t they the same now??
There are a few technical differences. Abstract classes can still do more in comparison to Java 8 interfaces:
- Abstract class can have a constructor.
- Abstract classes are more structured and can hold a state.
The good thing about this new feature of functional interfaces is that, where before we were forced to use an abstract class for the convenience methods, thus constraining the implementor to single inheritance, now we can have a really clean design with just the interface and a minimum of implementation effort forced on the programmer.
Conclusion
In this article we walked through the latest Functional Interfaces API, introduced in Java 8. We studied about what functional interfaces offer to us and how we can use them to empower our application and make our code cleaner and faster. In the java.util.Function package, there are over 45 functional interfaces present which are very useful once we become familiar with them. So go ahead and start using them to make better apps.
Check out my article on Abstract Classes in Java or
Visit our homepage to search and compare the best Java SDKs.
Recent Stories
Top DiscoverSDK Experts
Compare Products
Select up to three two products to compare by clicking on the compare icon () of each product.
{{compareToolModel.Error}}
{{CommentsModel.TotalCount}} Comments
Your Comment