Functional Interfaces in Java
A functional interface is an interface with only one abstract method thus exposing just one functionality.
Along with the only abstract method, a functional interface can contain any number of default methods.
The following code is an example of Functional Interface
@FunctionalInterface public interface SampleFunctionalInterface { void sampleMethod(String input); default void sampleDefaultMethod(String input){ System.out.println("Entered into the sampleDefaultMethod" + input); } }
An interface with a single abstract method can be marked as a functional interface by adding the annotation ‘@FunctionalInterface’ over the interface name. This annotation will make sure that the compiler shows an ‘Unexpected @FunctionalInterface annotation’ when someone tries to add more than one abstract method. This annotation can save a programmer from accidentally introducing a second abstract method inside a functional interface. The @FunctionalInterface annotation is not mandatory and is a recommendation as it helps to avoid compilation errors in the code.
Functional interfaces were introduced in Java 8 along with Lambda. Together, lambda and functional interfaces, provide us a shorter way to implement functionalities without the need for creating full-blown classes and methods.
For example, before Java 8, a program to instantiate and start a thread would look like this –
class SampleThread { public static void main(String args[]) { Thread myThread = new Thread(new Runnable() { @override public void run() { System.out.println(“A new thread is started”); } } myThread.start(); } }
After Java 8 the whole code of instantiating a Thread’s object and starting it can be minimized into the following code snippet –
class SampleThread { public static void main(String args[]) { new Thread(() -> {System.out.println(“A new thread is started”);}).start(); } }
As seen in the SampleThread class above, we are using the Runnable functional interface which contains only one abstract method run(). Thus, we can instantiate a Thread object and start it using the Runnable functional interface and its abstract method run(), in just one line of the program.
So, how does the concept of Functional Interface deal with Java’s arch enemy ‘The Diamond problem’. As we all know that extending multiple classes in Java is not allowed because of the diamond problem.
Suppose for a while that extending multiple classes were allowed in Java, then if two classes; say class A and class B, that had a method with similar signatures, then the class extending these two classes will never know which method to use between the two parent classes. To avoid such a situation, multiple inheritances are forbidden in Java and it will throw a compile-time error. Consider the sample below for showcasing the compiler issue that Java throws in case of multiple inheritances –
First parent class, notice the method myUniqueMethod(int, String)
public class ParentClass1 { public String myUniqueMethod(int firstValue, String secondValue) { System.out.println("firstValue and secondValue are :"+firstValue+" & "+secondValue); return "From_ParentClass1"; } }
Second parent class, notice the same method with similar signature myUniqueMethod(int, String)
public class ParentClass2 { public String myUniqueMethod(int primaryArg, String secondaryArg) { System.out.println("primaryArg and secondaryArg are :"+primaryArg+" & "+secondaryArg); return "From_ParentClass2"; } }
Now let’s create a child class and see what happens when we try to extend both these parent classes –
public class ChildClass extends ParentClass1, ParentClass2 { public static void main(String args[]) { System.out.println("I am confused due to two parents with similar methods. " + "I won't let that happen!"); } }
If you are using an IDE then it will start highlighting an error over the extends keyword, as in my case of using Eclipse. The syntax errors thrown on Eclipse are –
Multiple markers at this line - Syntax error on token "extends", delete this token - Syntax error, insert "ClassBody" to complete CompilationUnit - Syntax error, insert "}" to complete Block
This proves that multiple inheritances are prohibited in Java and trying to do which will cause syntax errors.
With the advent of default methods, there is a possibility of having a default method with a similar signature in two different interfaces and a child class implementing both these interfaces. How would the compiler know which parent class’ method to use? To avoid such a situation, it has been made mandatory to provide an implementation of the common default method of interfaces in the child class. So, the child class will have to implement the common default method found in multiple interfaces implemented by it.
Now revisiting the example shown above. Let’s consider ParentClass1 and ParentClass2 classes as interfaces and the child class implementing these interfaces. Here is how it would look now –
First parent interface –
public interface ParentInterface1 { public void doSomeStuff(); public default String myUniqueMethod(int firstValue, String secondValue) { System.out.println("firstValue and secondValue are :"+firstValue+" & "+secondValue); return "From_ParentClass1"; } }
Second parent interface –
public interface ParentInterface2 { public String performSomeJob(); public default String myUniqueMethod(int primaryArg, String secondaryArg) { System.out.println("primaryArg and secondaryArg are :"+primaryArg+" & "+secondaryArg); return "From_ParentClass2"; } }
The child class implementing the two interfaces with the common default methods would look like –
public class ChildClass implements ParentInterface1, ParentInterface2 { @Override public String performSomeJob() { System.out.println("Performing a job"); return "PerformedSomeJob"; } @Override public void doSomeStuff() { System.out.println("Doing some stuff"); } public static void main(String args[]) { System.out.println("I am confused due to two parents with similar methods. " + "I won't let that happen!"); } }
In which case the IDE will complain about the missing implementation of the common default method by showing the below error –
Duplicate default methods named myUniqueMethod with the parameters (int, String) and (int, String) are inherited from the types ParentInterface2 and ParentInterface1
On compiling the ChildClass.java would throw an error with the message –
ChildClass.java:2: error: class ChildClass inherits unrelated defaults for myUniqueMethod(int,String) from types ParentInterface1 and ParentInterface2 public class ChildClass implements ParentInterface1, ParentInterface2 { ^ 1 error
Adding the implementation for the common default method, myUniqueMethod(int, String), fixes the compilation error. Here is the ChildClass.java after implementing the common default method –
public class ChildClass implements ParentInterface1, ParentInterface2 { @Override public String performSomeJob() { System.out.println("Performing a job"); return "PerformedSomeJob"; } @Override public void doSomeStuff() { System.out.println("Doing some stuff"); } @Override public String myUniqueMethod(int primaryArg, String secondaryArg) { return "my own uniqueMethod!"; } public static void main(String args[]) { System.out.println("I am confused due to two parents with similar methods. " + "I won't let that happen!"); } }
To summarize, any interface with a single abstract method is a functional interface and its implementation can be used as a lambda expression. Optionally, a programmer can also enforce it to be a functional interface by using the @FunctionalInterface over the interface name while coding. In addition to the only abstract method, a functional interface can have any number of default methods.