Functional Programming in Java
Code For Maintainability
1
KrkDataLink
Kraków
2017-02-15
@marcinstepien
www.smart.biz.pl
Marcin Stepien
Developer, Consultant
@marcinstepien
www.smart.biz.pl
2005
www.whenvi.com
2014
2
What is here
3
Code for maintainability
Functional Programing intro
Object vs Functional
Code examples
Closure Lisp
Frege kind of Haskell
Scala
Java 8, Kotlin, Groovy
Functional Programming on JVM
6
➔ Streams
➔ Lambda expressions
➔ Functions as first class citizens
➔ Functional Interfaces
8
Java 8 FP
➔ No side effects
➔ Final variables
➔ y = f(x) always same y for given x
9
(pure) function
Function<Integer,Integer> increase = x -> x + 1;
#parameter -> body
10
Java 8 function
11
Functional interface Function descriptor
Predicate<T> T -> boolean
Consumer<T> T -> void
Function<T,R> T -> R
Supplier<T> () -> T
UnaryOperator<T> T -> T
BinaryOperator<T> (T,T) -> T
BiPredicate<L,R> (L,R) -> boolean
BiConsumer<T,U> (T,U) -> void
BiFunction<T,U,R> (T,U) -> R
#instead ofRunnable task =new Runnable() {
public void run() {System.out.println(”no arg consumer” )
}}
12
Replace ceremony with Lambda
Runnable task = () -> { System.out.println(”no arg consumer” ); };
13
Internal iterators
#instead of loopsfor(String name : names) {
System.out.println(name)}
#use internal iteratorsnames.forEach(e -> System.out.println(e))
#with method referencenames.forEach(System.out::println)
public int getSumOfEvens(List<Integer> nums){Integer sum = 0;for(Integer num : nums) {
if(num != null && num % 2 == 0) {sum += num;
}}return sum;
}
14
Fight verbosity with streamsnums.stream()
.filter(Objects::nonNull) .filter( number -> number % 2 == 0) .reduce(0, Integer::sum);
15
Passing functionspublic int getSum(List<Integer>nums,Predicate<Integer> filter,Bifunction<Integer,Integer,Integer> accumulator){
return nums.stream() .filter(filter)
.filter(Objects::nonNull) .reduce(0, accumulator);
}
Predicate<Integer> isEven = n -> n % 2 == 0;
int sum = getSum(numbers, isEven, Integer::sum);
16
Map reduce, lazy evaluation
public int getTranactions(List<User> users) { return users.stream() # or parallelStream() .filter(u -> u.isActivated()) .map( u -> u.getTranTotal()) .reduce(0, Integer::sum);} #terminal op
public Stream<Integer> get(List<User> users) { #no computation is happening here return users.stream() .filter(u -> u.isActivated()) .map( u -> u.getTranTotal());}
17
Lazy evaluation, infinite series
Stream<Integer> infiniteSeries = Stream.iterate(1, e -> e + 1)
18
Slimmer patterns: decorator
← https://en.wikipedia.org/wiki/Decorator_pattern#Second_example_.28coffee_making_scenario.29
← Hierarchy of types
can be replaced with Function Composition:● .compose(Function f) ● .andThan(Function f)
19
Function composition
class Barista {public static Coffee makeCoffee(Coffee baseCoffee, Function<Coffee, Coffee>... addIngredients) { return Stream.of(addIngredients) .reduce( Function.<Coffee>identity(), //or Function::andThan (addIngr1, addIngr2) -> addIngr1.andThan(addIngr2))
Coffee coffee = Barista.makeCoffee( new Arabica(), Coffee::withMilk, Coffee::withSprinkles));
Object vs Functional
20
● Imperative
● Mutability
● Lower abstraction
● Reveals impl. details
● Eager by default
● nulls
● Hierarchy of types
● Declarative
● Favors Immutability
● Expressive
● Hides impl. details
● Lazy by default
● Optionals
● Function composition