Java Functional Programming : 3. The java.util.function Library
This is the multi part series on Java Functional Programming
3. The java.util.function Library
In most cases you might not need to declare your own functional interface as Java gives you a standard set of interfaces to save your time. In case you’d like to work with a function of two parameters of the same type, you could probably use the interface java.util.function.BinaryOperator<T> for the same activity. (More on the package java.util.function is to follow). As a shortcut, you’d probably prefer to keep things simple without the need of an extra interface declaration. Lets look at the constructs below:
BinaryOperator<Integer> binAdd = (a, b) -> a + b;
System.out.println("Bin
Add 2, 3 = " + binAdd.apply(2, 3));
|
The lambda
expressions can be passed as a parameter into the methods. Let us define a method
which doubles the value given by the lambda expression.
private static int doubleVal(int a, int b, IntMathOperation op){
return op.operation(a, b) * 2;
}
|
You could pass the expression
directly in the parameter and they would give back the output based upon
the operation you passed. For example the code below
System.out.println("Double
Val Add (3, 2) = " + doubleVal(3,
2, (a, b) -> a + b));
System.out.println("Double
Val Sub (3, 2) = " + doubleVal(3,
2, (a, b) -> a - b));
System.out.println("Double Val Mul (3, 2) = " + doubleVal(3, 2, (a, b) -> a * b));
|
would give
the output as:
Double Val Add
(3, 2) = 10
Double Val Sub
(3, 2) = 2
Double Val Mul (3, 2) = 12
Not only the
input parameters, the lambda expressions can also be returned back as the
output of a method in the form of a functional interface. Have a look at the
code below. The method doubleOp (the method is made static
so that we don’t have to initialize the class) returns a functional interface
as an output. The method takes in a functional interface as a parameter
and doubles the value and passes back as a functional interface.
@FunctionalInterface
public interface MyOperation {
public int op(int i, int j);
}
public static MyOperation doubleOp(MyOperation mo){
return (a, b) -> mo.op(a, b) * 2;
}
public static void main(String... args) {
System.out.println("------
Printing Double Operation -----");
MyOperation mo = doubleOp((a, b) -> a + b);
System.out.println(mo.op(6, 7));
System.out.println(doubleOp((a, b) -> a + b).op(6,
7));
}
|
Running the
above program yields the output
------ Printing
Double Operation -----
26
26
We can take
the output of this method and treat is exactly like a functional interface and
pass in the values to apply to the method. The last line of the program shows
that we can also pass in the lambda function and the values directly into the
parameter. A little more complex program is mentioned below. The code is self
explanatory. This way you can chain multiple operations (or expressions).
@FunctionalInterface
public interface MyOperation {
public int op(int i, int j);
}
public static MyOperation multOps(MyOperation mo1, MyOperation
mo2){
return (a, b) -> mo1.op(a, b) * mo2.op(a, b);
}
public static void main(String... args) {
System.out.println("-----
Printing Multiple of Operations ------");
MyOperation op1 = (a, b) -> a - b;
MyOperation op2 = (a, b) -> a * b;
MyOperation mulOp = multOps(op1, op2);
System.out.println(mulOp.op(6, 4));
System.out.println(multOps((a, b) -> a - b, (a, b) -> a * b).op(6,
4));
}
|
The output
of this program would be
----- Printing
Multiple of Operations ------
48
48
Now that we
have covered the basics let us now try to understand the many functional interfaces
in the package java.util.function. We have seen the interface java.util.function.BinaryOperator<T>
earlier and let us introduce the family here. As we have seen, since the body
is not defined, a lot of functional interfaces could be reused. This package
defines those standard set of interfaces which can be reused without worrying
to create your own. These are the ones which are used by the standard java
library set, so it makes sense to get acquainted with the package. This package
has four categories of functional interfaces and many variants of each of them.
We will study the group first and try to understand them. This will make it
easy to remember and task simpler, going forward. There are four groups of functional
interfaces
in the package – the predicates, the consumers, the suppliers and the
functions. It’s easy to remember. Memorize the following phrase – “Suppliers
get something which Consumers accept, test
it by Predicates and apply Function to it”. That’s
it. Apart from the four categories the phrase also contains the functionality
each group is expected to perform. Suppliers are supposed to get us something,
Consumers accept, Predicates must be used to test it and Functions should be
applied.
These
functional interfaces are typed, so that they can be used with
any class type. The supplier, consumer and predicate are defined with only one type
of class, but the function needs two of them. The Suppliers has the method get
which takes in no parameters but returns the object of the type, Consumers
have a method accept which takes in the object of the type but do not return
anything. The Predicates have a method test which returns a boolean
value and take in the object of the type as input, and the Functions have the method
apply
which take in one type and return another (remember the above phrase). If did
not get it now don’t worry. We will go through the four major functional
interfaces and see their usage. Then you can come back and recite the phrase
again. Let us have class “Chair”, which has come attributes (Described below).
We will use the same class to describe each category of the functional
interface.
public class Chair {
private String brand;
private int price;
private boolean withCushion;
private
Chair(String brand, int price, boolean withCushion){
super();
this.brand = brand;
this.price = price;
this.withCushion = withCushion;
System.out.println("Chair
Created ... !!");
}
/**
* Returns an immutable object chair.
*/
public static Chair createChair(String brand, int price, boolean withCushion){
return new Chair(brand, price, withCushion);
}
public final String getBrand(){
return brand;
}
public final int getPrice() {
return price;
}
public final boolean isWithCushion() {
return withCushion;
}
@Override
public final String toString(){
return "brand=" + brand + ",
price=$" + price + ".00, With Cushion?=" +
withCushion;
}
@Override
public boolean equals(Object chair2){
if (!(chair2 instanceof Chair))
return false;
Chair ch2 =
(Chair) chair2;
return this.price == ch2.price && this.withCushion == ch2.withCushion;
}
}
|
Remember
what each functional interface does and refer to the code below
// supplier
creates a new chair
Supplier<Chair>
createChair = ( ) ->
Chair.createChair("Java
Chair", 200, true);
// consumer
does an operation on the chair
Consumer<Chair>
printDetails = (c) -> System.out.println("Printing Details => " + c);
// checks
some parameter of the chair
Predicate<Chair>
isCushioned = (c) -> c.isWithCushion();
// gets the
wholesale price of a chair
//
wholesale price is 80% of the price rounded to an integer
Function<Chair,
Integer> wholeSalePrcie = (c) -> Math.round(0.8f * c.getPrice());
// using
supplier
Chair c = createChair.get();
// using
consumer
printDetails.accept(c);
// using
predicate
boolean isWithCushion = isCushioned.test(c);
System.out.println("Is
With Cushion=" + isWithCushion);
// using
function
int wcPrice = wholeSalePrcie.apply(c);
System.out.println("Whole Sale Price=$" + wcPrice + ".00");
|
- The ‘Supplier’ functional interface has a method (get) which returns a type Chair, but does not take in any parameter. Usually this type of functional interface would be used for creation of an object. In most applications this would be the object initialized from values taken from a data repository (such as XML, file system, database, etc)
- The ‘Consumer’ functional interface has a method (accept) which takes in a Chair object, and performs some operation on it. This method does not return any value, so it can be used for data persistence or an external system call or any other side-effect. In this case it simply prints the object Chair.
- The ‘Predicate’ functional interface has a method (test) which takes in the chair object and tests something chair. It can be used to filter objects as well. In this example it will simply check a property of Chair.
- The ‘Function’ functional interface has a method (apply) which takes in the object Chair and applies some operation to it and returns the object of a different type. In the example it returns an integer, which is the wholesale price.
The above
code execution prints the following, on the console:
Chair Created
... !!
Printing
Details => brand=Java Chair, price=$200.00, With Cushion?=true
Is With
Cushion=true
Whole Sale Price=$160.00
Now that we
have a fairly good idea of the types and what each one does, let us have a look
at each of these four functional interfaces in detail.
Yes, there are more to it than just the abstract methods in the interface. Those
interfaces
come with default and static methods as well. The Supplier
interface has just the abstract method so it warrants no other discussion.
Let’s discuss others in the coming topics.
<< Prev (Lambda Expressions & Java Functional Interfaces) | Next (Consumer) >>
Comments
Post a Comment