Java Custom Functional Interface
April 06, 2019
This page will walk through Java custom functional interface example. Java provides @FunctionalInterface
annotation to create functional interface. @FunctionalInterface
is available since Java 8. A functional interface has exactly one abstract method. Functional interface can be initialized using lambda expressions, method references, or constructor references. A functional interface can have default methods. Functional interfaces can also be created by inheriting another functional interface. Java provides built-in functional interfaces such as Supplier
, Consumer
, Predicate
etc.
Here on this page we will create our custom functional interfaces using
@FunctionalInterface
annotation. We will create functional interfaces with generics, default methods and by inheritance in our example. We will also provide examples to initialize functional interfaces using lambda expressions, method references, or constructor references. Now let us discuss to create custom functional interfaces step by step.
Contents
- @FunctionalInterface
- Create Functional Interface
- Instantiate Functional Interface using Lambda Expression
- Instantiate Functional Interface using Method Reference
- Instantiate Functional Interface using Constructor Reference
- Functional Interface with Default Methods
- Functional Interface with Generic and Default Methods
- Functional Interface Inheritance
- Reference
- Download Source Code
@FunctionalInterface
1.@FunctionalInterface
annotation is used to create a functional interface.
2. A functional interface has exactly one abstract method.
3. Default methods of interface are not counted as abstract as they have implementation.
4. If functional interface declares an abstract method overriding one of the public methods of Java
Object
class, that will also not be counted.
5. The instances of functional interface can be created by using lambda expressions, method references, or constructor references.
Create Functional Interface
To create our functional interface, we need to create an interface annotated with@FunctionalInterface
and exactly one abstract method. An abstract method within an interface is followed by a semicolon, but no braces.
Calculator.java
package com.concretepage; @FunctionalInterface public interface Calculator { long calculate(long num1, long num2); }
Calculator
interface with abstract method calculate
. The interface Calculator
is annotated with @FunctionalInterface
and in this way we have created a functional interface i.e. Calculator
. We can instantiate a functional interface using lambda expressions, method references, or constructor references.
Instantiate Functional Interface using Lambda Expression
Here we will instantiate a functional interface using lambda expression. Find the lambda expression syntax.(Argument part) -> Body part
Calculator
as following.
Calculator calc = (n1, n2) -> n1 + n2;
calculate
has been defined with two arguments. To get the result, we will call the method of our functional interface.
System.out.println(calc.calculate(30, 50));
Passing Functional Interface as Method Argument using Lambda Expression:
Now create a method in a class whose argument will be our functional interface type as following.
public long process(Calculator calc) { return calc.calculate(this.firstNum, this.secondNum); }
MyNumber.java
package com.concretepage; public class MyNumber { private long firstNum; private long secondNum; public MyNumber(long firstNum, long secondNum) { this.firstNum = firstNum; this.secondNum = secondNum; } public long process(Calculator calc) { return calc.calculate(this.firstNum, this.secondNum); } //setters getters }
process
method in the above class. Suppose we have a list of MyNumber
as following.
List<MyNumber> list = new ArrayList<>(); list.add(new MyNumber(100, 40)); list.add(new MyNumber(300, 60)); list.add(new MyNumber(60, 20));
Example-1:
Here we are creating object of our functional interface and then passing it as argument for summation.
Calculator calc = (n1, n2) -> n1 + n2; for(MyNumber myNumber: list) { System.out.println(myNumber.process(calc)); }
140 360 80
Here we are directly passing lambda expression as an argument for multiplication.
for(MyNumber myNumber: list) { System.out.println(myNumber.process((n1, n2) -> n1 * n2)); }
4000 18000 1200
Here we are performing division.
for(MyNumber myNumber: list) { System.out.println(myNumber.process((n1, n2) -> n1 / n2)); }
2 5 3
Instantiate Functional Interface using Method Reference
Method reference invokes method using (::) sign. Suppose we have a classMyNumber
and a static method add
then we can call it using class name.
MyNumber::add
add
is not a static method then we can call this method using instance of the class. Suppose myNumber
is the instance of MyNumber
class and add
is non-static method, then we call it using instance as given below.
myNumber::add
To create instance of functional interface using method reference we need to create a method with same method declarations as abstract method. The method in our functional interface
Calculator
is as following.
long calculate(long num1, long num2);
add
and multiply
in our utility class with same declarations as abstract method of functional interface. Find the utility class.
Utility.java
package com.concretepage; public class Utility { public static long add(long num1, long num2) { return num1 + num2; } public static long multiply(long num1, long num2) { return num1 * num2; } }
Utility
class as following.
Calculator calc = Utility::add; System.out.println(calc.calculate(30, 50));
Passing Functional Interface as Method Argument using Method Reference:
Now let us use our
MyNumber
class with method reference. We have already created MyNumber
class and a list of its objects above. Now find the use of method reference. First we are using utility add
method.
for(MyNumber myNumber: list) { Calculator calc = Utility::add; System.out.println(myNumber.process(calc)); }
System.out.println(myNumber.process(Utility::add));
140 360 80
multiply
method.
for(MyNumber myNumber: list) { System.out.println(myNumber.process(Utility::multiply)); }
4000 18000 1200
process
method of MyNumber
class.
public long process(Calculator calc) { return calc.calculate(this.firstNum, this.secondNum); }
process(Utility::add)
and process(Utility::multiply)
then our functional interface Calculator
is instantiated with the definition of add
and multiply
method of Utility
class respectively. When calculate
method is called with given arguments then we get results.
Instantiate Functional Interface using Constructor Reference
Here we will instantiate a functional interface using constructor reference. We need to usenew
keyword for constructor reference. Find the constructor reference for Utility
class.
Utility::new
TaskHandler.java
package com.concretepage; @FunctionalInterface public interface TaskHandler { void get(String tname); }
Utility
class as given below.
Utility.java
public class Utility { public Utility(String taskName) { System.out.println(taskName); } ------ }
TaskHandler taskHandler = Utility::new; taskHandler.get("Task 1");
Functional Interface with Default Methods
We can create default methods in our functional interface. Find the functional interfaceWorship
.
Worship.java
package com.concretepage; import java.util.Objects; @FunctionalInterface public interface Worship { void chant(String name); default Worship again(Worship w) { return (name) -> { Objects.requireNonNull(w); chant(name); w.chant(name); }; } }
again
. The type of parameter is Worship
itself. The return type is also Worship
. Now we have to define our default method. As the default method is returning Worship
, we need to return a function which defines its abstract method i.e. chant
. Now look into the definition of default method.
default Worship again(Worship w) { return (name) -> { Objects.requireNonNull(w); chant(name); w.chant(name); }; }
Objects.requireNonNull
checks that the specified object reference is not null. In the above code the method chant(name)
is the method of caller Worship
instance. w.chant(name)
is of the argument Worship
instance. If we call again
method many times, the result will be chained. Now let us run the example.
Worship worship = (name) -> System.out.println(name); worship.again(worship).again(worship).chant("Ram");
Ram Ram Ram
Worship
with some changes and then run it.
Worship worship = (name) -> { System.out.println(name); System.out.println(name); }; worship.again(worship).again(worship).chant("Ram");
Ram Ram Ram Ram Ram Ram
Functional Interface with Generic and Default Methods
We will create some functional interfaces with generics here. We will also add default methods to use those functional interfaces.Functional interface 1:
DataCombiner.java
package com.concretepage; @FunctionalInterface public interface DataCombiner<T> { String combine(T t); }
ExtraInfoProvider.java
package com.concretepage; @FunctionalInterface public interface ExtraInfoProvider<R> { R provideMore(R r); }
Now find the
InfoProvider
functional interface that will use DataCombiner
and ExtraInfoProvider
in its default methods.
InfoProvider.java
package com.concretepage; import java.util.Objects; @FunctionalInterface public interface InfoProvider<T, R> { R provide(T t); default InfoProvider<T, R> addMore(ExtraInfoProvider<R> more) { return (T t) -> { Objects.requireNonNull(more); R r = provide(t); return more.provideMore(r); }; } default DataCombiner<T> addCombiner(DataCombiner<R> combiner) { return (T t) -> { Objects.requireNonNull(combiner); return combiner.combine(provide(t)); }; } }
provide
and two defaults methods addMore
and addCombiner
. The default method addMore
is returning InfoProvider
, so within addMore
we will return a function definition of provide
abstract method of InfoProvider
functional interface. Objects.requireNonNull
checks that the specified object reference is not null.
In the
addCombiner
method, we are returning DataCombiner
, so within this method we will return function definition of combine
abstract method of DataCombiner
functional interface.
Suppose we have two classes
Employee
and Project
as following.
Project.java
public class Project { private String pname; private String teamLead; private String location; public Project(String pname, String teamLead) { this.pname = pname; this.teamLead = teamLead; } //getters and setters }
public class Employee { private int id; private String name; public Employee(int id, String name) { this.id = id; this.name = name; } //getters and setters }
DataCombiner
, ExtraInfoProvider
, InfoProvider
and run it.
DataCombiner<Project> dataCombiner = (Project p) -> { if(p.getLocation() == null) { return p.getPname()+" : " + p.getTeamLead(); } else { return p.getPname()+" : " + p.getTeamLead() + " : " + p.getLocation(); } }; InfoProvider<Employee, Project> infoProvider = (Employee emp) -> { if(emp.getId() > 100) { return new Project("ABCD", emp.getName()); } else { return new Project("PQRS", emp.getName()); } }; InfoProvider<Employee, Project> infoProvider = (Employee emp) -> { if(emp.getId() > 100) { return new Project("ABCD", emp.getName()); } else { return new Project("PQRS", emp.getName()); } }; String s = infoProvider.addMore(extraInfoProvider) .addCombiner(dataCombiner).combine(new Employee(50, "Mahesh")); System.out.println(s);
PQRS : Mahesh : Noida
Functional Interface Inheritance
We can create functional interface by inheriting existing one. Suppose we have a functional interface as following.DataCombiner.java
package com.concretepage; @FunctionalInterface public interface DataCombiner<T> { String combine(T t); }
DataReceiver
functional inheritance by extending DataCombiner
and add a default method.
DataReceiver.java
package com.concretepage; import java.util.Objects; @FunctionalInterface public interface DataReceiver<T> extends DataCombiner<T> { default void receive(TaskHandler handler, T t) { Objects.requireNonNull(handler); handler.get(combine(t)); } }
receive
, we are passing TaskHandler
functional interface. Find the TaskHandler
.
TaskHandler.java
package com.concretepage; @FunctionalInterface public interface TaskHandler { void get(String tname); }
DataReceiver
and TaskHandler
and then run it.
DataReceiver<Employee> dataReceiver = (Employee emp) -> emp.getId() + "-" + emp.getName(); TaskHandler tskHandler = (res) -> System.out.println(res); dataReceiver.receive(tskHandler, new Employee(101, "Krishna"));
101-Krishna