The following is a series of posts about "functional programming in Java" which is the result of my understanding by reading the book "Java 8 in Action: Lambdas, Streams, and Functional-style Programming, by Alan Mycroft and Mario Fusco".
1. Why functional programming?
2. Functional programming in Java 8
3. Java 8 - Using Functions as Values
4. Java 8 - Persistent data structure
1. Why functional programming?
2. Functional programming in Java 8
3. Java 8 - Using Functions as Values
4. Java 8 - Persistent data structure
In general, the phrase "functional-style programming" means the behavior of functions should be like that of mathematical-style functions - no side effects.
In programmers' point of views, functions may be used like other values: passed as arguments, returned as result, and stored in data structures. That means we can use the :: operator to create a method reference, and lambda expressions to directly express function values. For example:
//method reference
Function<String, Integer> strToInt = Integer::parseInt;
//lambda expression
Comparator<Integer> comparator = (a, b) -> a.compareTo(b);
There are two practical techniques to apply in our codebase.
Higher-order functions
For example:Comparator<Apple> c = Comparator.comparing(Apple::getWeight);
- Take one or more functions as parameter
- Return a function as result
Currying
Brain overload! For example, you want to convert Celsius to Fahrenheit, the formula is f(x) = x *9/5 + 32. Not only this case but we can see the basic pattern of all unit conversion is as follow:
static double converter(double x, double f, double b) {This method is a bit too general. Obviously, we can use it to apply for all unit conversion but it would be tedious and you might accidentally mistyped them. There is an better approach:
return x * f + b;
}
static DoubleUnaryOperator curriedConverter(double f, double b){Instead of passing all the arguments x, f and b all at once to the converter method, you only ask for the arguments f and b and return another function, which given an argument x return x*f + b.
return (double x) -> x * f + b;
}
Why is it the better approach? This enables you to reuse the convention logic and create different functions with different convention factors:
DoubleUnaryOperator convertCtoF = curriedConverter(9.0/5, 32);Instead of three different method for each conversion factor:
DoubleUnaryOperator convertUSDtoGBP = curriedConverter(0.6, 0);
DoubleUnaryOperator convertKmtoMi = curriedConverter(0.6214, 0);
static double convertCtoF(double x, double f, double b) {Foo: If so, for what?
return x * f + b;
}
static double convertUSDtoGBP(double x, double f, double b) {
return x * f + b;
}
static double convertUSDtoGBP(double x, double f, double b) {
return x * f + b;
}
Bar: It helps you modularize functions and reuse code!
Reference
[1]. Alan Mycroft and Mario Fusco, Java 8 in Action: Lambdas, Streams, and Functional-style Programming. Part 4. Beyond Java 8.