Java 8 is packed with some new features in language level. In this blog post I hope to give an introduction to behavior parameterization with samples using lambda expressions. I will first describe a simple scenario and give a solution with java 7 features and then improve that solution with java 8 features.
What is behavior parameterization?
Behavior parameterization is a technique to improve the ability to handle changing requirements by allowing a caller of a function to pass custom behavior as a parameter. In a simple note, you can pass a block of code as an argument to another method, which will parameterize the behavior based on the passed code block.
Sample scenario:
Assume a scenario of a Company with set of Employees. The management of the company need to analyze the details of the employees of the company to identify/categorize employees into set of groups. (eg: group based on age, gender, position etc).
Below is a sample code for categorizing employees based on age and gender with java 7.
Solution 1 - Using java 7
There are 2 methods filterByAge() and filterByGender() which follows the same pattern except one logic which is the logic inside the if statement. If we can parameterize the behavior inside the if block, we could use a single method to perform both filtering options. It improves the ability to tell a method to take multiple strategies as parameters and follow them internally as per the requirements.
Lets try to reduce the code to a single method using anonymous classes. We are still using java 7 and no java 8 features were used.
Solution 2 - Improved with anonymous classes
Instead of maintaining two methods we have introduced a new method “filterEmployee” which takes 2 arguments an employee inventory and an EmployeePredicate. EmployeePredicate is a customized interface that has a single abstract method test() which takes an Employee object and returns a boolean. Then we have used 2 implementations of EmployeePredicate interface as anonymous classes to pass the behavior as per the requirement.
We have changed our program from solution-1 to solution-2 with following steps:
- We have reduced 2 methods to a single method and improved that method to accept a behavior. (This is an improvement)
- We had to introduce a new custom interface EmployeePredicate and use anonymous classes to pass the behaviour. (This is not a good enough and verbose. We need to improve this)
Functional Interfaces
Functional interface is an interface that has only a one abstract method. (Similar to what we have introduced in the previous solution, EmployeePredicate interface). Functional interface can have other default methods (which is another new feature introduced in java8) as long as it includes a single abstract method.
Sample functional interfaces:
As per our latest solution with anonymous classes, we need to create our own interface that includes a method accepting an object of our preference and return some output. But with java 8 there are some generic functional interfaces that were newly introduced. We can reuse them to pass different behaviors without creating our own interfaces. I have listed some of them below:
- java.util.function.Predicate<T> interface has a one abstract method “test()” that takes an Object of type T and returns a boolean.
- Eg: as per our scenario We take Employee as an object and return a boolean to indicate if the employee’s age is less than 30.
- java.util.function.Consumer<T> interface has a one abstract method “accept()” that takes an Object of type T and does not return anything
- Eg: Assume we need to print all the details of a given Employee. But not return anything. We can use Consumer interface.
- java.util.function.Function<T,R> interface has a one abstract method “apply()” that takes an Object of Type T and returns an Object of type R.
- Eg: Assume we need to take Employee object and return the employee ID as an integer.
What should be the functional interface that we need to use to improve our existing solution in the employee categorizing scenario?
Lets try to use Predicate functional interface and improve the solution.
Solution 3 - Using java 8 functional interfaces
So far we have changed our program from solution-2 to solution-3 with the steps below:
- We have removed the customized interface EmployeePredicate and used an existing Predicate functional interface from java 8 - (This is an improvement and we have reduced an interface)
- We still use the anonymous functions. (still verbose and not good enough)
Lambda expressions:
We can use lambda expressions in any place we need to pass a functional interface. Lambda expressions can represent behavior or pass code similar to anonymous functions. It can has a list of parameters, method body and a return type.
We can describe a lambda expression as a combination of 3 parts as below:
- List of parameters
- An arrow
- Method body
Consider the sample lambda expression below:
(Employee employee) ---> employee.getAge() < 30
This sample shows how we can pass a behavior to a Predicate interface that we have used in solution-3. Lets first analyze a anonymous class we have used.
We are implementing the Predicate functional interface that has a single abstract method. This abstract method takes an Object as parameters and return a boolean value. In the lambda expression we have used:
- (Employee employee) : Parameters for the abstract function of Predicate interface
- ---> : Arrow separates the list of parameters from the body of lambda
- employee.getAge() < 30 : This is the body of the abstract method of the predicate. The result of the body is a boolean value. Hence the above lambda expression returns a boolean value.
Sample lambda expressions:
- (Employee employee) ---> System.out.println(“Employee name : ” + employee.name + “\n Employee ID: ” + employee.id)
- This a possible implementation of the Consumer functional interface that has a single abstract method that accepts an object and return void.
- (String s) ---> s.length()
- This a possible implementation of the Function functional interface that has a single abstract method that accepts an object and return another object.
- () → new Integer(10)
- This lambda expression is for a functional interface that has a single abstract method with no arguments and return an integer.
- (Employee employee, Department dept) ---> {
If (dept.getEmployeeList().contains(employee.getID)) {
System.out.println(“Employee : ” + employee.getName());
}
}
- This lambda expression is for a functional interface that has a single abstract method with 2 arguments of type object and return a void.
Let's rewrite the solution using lambda expressions.
Solution - 4 (Using lambda expressions)
Solution-4 can further improved using method references. I have not discussed method references with this blog post and will not include that solution here.