In this article, you are going to learn how to extend the java8 existing Function interface to provide additional features by using the Function
interface without affecting the existing functionality. This is the best example of open for extension close for modification, one of the SOLID principles.
This article demonstrates practical examples, usage of the apply, addThen, and compose methods from the Function interface.
Let us take some testimonials and use cases to use this interface.
Represents a function that accepts one argument and produces a result.
Existing use case:
You have some existing code with one interface TestInterface which takes testInput and returns the testOutput.
- it can work with different inputs, say TestOtherInput, and
- be able to produce different outputs, say TestOtherOutput, and
- the change should be done in a way that no existing code should break. As per the SOLID principle.
Let us see the code snippet of the existing interface and its implementation.
interface TestInterface {
TestOutput transform(TestInput testInput);
}
class TestInterfaceImpl implements TestInterface {
@Override
public TestOutput transform(TestInput testInput) {
// does processing or
// calls external systems to build testOutput
TestOutput testOutput = ...
return testOutput;
}
}
class TestInput {
// some fields and methods
}
class TestOutput {
// some fields and methods
}
class TestClient {
TestInterface testInterface;
TestOutput testClientMethod(TestInput testInput) {
return testInterface.transform(testInput);
}
}
This looks very simple and this is achievable using different ways.
Functional interface way:
Now let us change the above interface implementation by extending the Function interface so that you will get existing methods of interface as well support of functional style programming. Extend the interface and implement the apply method from Function interface.
interface TestInterface extends Function<TestInput, TestOutput> {
default TestOutput apply(TestInput testInput) {
return transform(testInput);
}
TestOutput transform(TestInput testInput);
}
As this is a functional interface so we have to provide some default implementation.
As with the above code changes, there is a change in the existing functionality and nothing will break. we did TestInterface can now work with any input types and can produce any output types.
Support different Input and Output:
In the above code, you have seen the usage of apply method only with specific input and specific output but if you want to support and transform testInput and testOtherInput and produce different output rather than testOutput then you have to use compose() and andThen() method. Before that please have a look at the below points.
The compose method from Function is used to transform TestOtherInput to TestInput.
The andThen method from the Function interface transforms the TestOutput to TestOtherOutput.
The TestOtherInput to TestInput transformation logic lives inside the toTestInput() instance method on TestOtherInput.
This logic is passed into compose method via the method references feature introduced in Java 8. TestOtherInput::toTestInput is a method reference.
The apply method from Function is called to build TestOutput from TestInput.
The transformation logic for TestOutput to TestOtherOutput resides in the TestOtherOutput class as a static factory method. It is provided to the andThen method using the method references feature of Java 8.
Let us see the code snippet.
Input:
class TestOtherInput {
// some implementation
TestInput toTestInput() {
// logic to build TestInput from this object
TestInput testInput = ...
return testInput;
}
}
Output:
class TestOtherOutput {
static TestOtherOutput fromTestOutput(TestOutput testOutput) {
// logic to build TestOtherOutput from TestOutput
TestOtherOutput testOtherOutput = ...
return testOtherOutput;
}
}
Client implementation:
class TestClient {
TestInterface testInterface;
TestOtherOutput testClientMethod(TestOtherInput testOtherInput) {
TestOtherOutput testOtherOutput = testInterface
.compose(TestOtherInput::toTestInput)
.andThen(TestOtherOutput::fromTestOutput)
.apply(testOtherInput);
return testOtherOutput;
}
}
Conclusion:
In this short article, we have how we can use the Function functional interface to extend the existing interface capabilities without affecting existing functionality. You can add the new features with minimal effort and by implementing the SOLID principle.
More such articles:
https://www.youtube.com/channel/UCiTaHm1AYqMS4F4L9zyO7qA
==========================**=========================
If this article adds any value for you then please clap and comment.
Let’s connect on Stackoverflow, LinkedIn, & Twitter.