KEMBAR78
Pragmatic functional refactoring with java 8 | PDF
Pragmatic Functional 
Refactoring with Java 8 
Raoul-Gabriel Urma 
Richard Warburton
First-class Functions 
Currying 
Immutability 
Optional Data Types 
Conclusions
Step 1: filtering invoices from Oracle 
public List<Invoice> filterInvoicesFromOracle(List<Invoice> invoices){ 
List<Invoice> result = new ArrayList<>(); 
for(Invoice invoice: invoices){ 
if( invoice.getCustomer() == Customer.ORACLE){ 
result.add(invoice); 
} 
} 
return result; 
}
Step 2a: abstracting the customer 
public List<Invoice> filterInvoicesFromCustomer(List<Invoice> invoices, 
Customer customer){ 
List<Invoice> result = new ArrayList<>(); 
for(Invoice invoice: invoices){ 
if( invoice.getCustomer() == customer){ 
result.add(invoice); 
} 
} 
return result; 
}
Step 2b: abstracting the name 
public List<Invoice> filterInvoicesEndingWith(List<Invoice> invoices, 
String suffix){ 
List<Invoice> result = new ArrayList<>(); 
for(Invoice invoice: invoices){ 
if( invoice.getName().endsWith(suffix)){ 
result.add(invoice); 
} 
} 
return result; 
}
Step 3: messy code-reuse! 
private List<Invoice> filterInvoicesBad(List<Invoice> invoices, 
Customer customer, 
String suffix, 
boolean flag){ 
List<Invoice> result = new ArrayList<>(); 
for(Invoice invoice: invoices){ 
if(( flag && invoice.getCustomer() == customer) 
|| (!flag && invoice.getName().endsWith(suffix))){ 
result.add(invoice); 
} 
} 
return result; 
}
Step 4a: modeling the filtering criterion 
public Predicate<T> { 
boolean test(T t); 
} 
Invoice boolean 
Predicate<Invoice>
Step 4b: using different criterion with objects 
private List<Invoice> filterInvoices(List<Invoice> invoices, 
Predicate<Invoice> p){ 
List<Invoice> result = new ArrayList<>(); 
for(Invoice invoice: invoices){ 
if( p.test(invoice)){ 
result.add(invoice); 
} 
} 
return result; 
}
Step 4b: using different criterion with objects 
List<Invoice> specificInvoices = 
filterInvoices(invoices, new FacebookTraining()); 
private class FacebookTraining implements Predicate<Invoice> { 
@Override 
public boolean test(Invoice invoice) { 
return invoice.getCustomer() == Customer.FACEBOOK 
&& invoice.getName().endsWith("Training"); 
} 
}
Step 5: method references 
public boolean isOracleInvoice(Invoice invoice){ 
return invoice.getCustomer() == Customer.ORACLE; 
} 
public boolean isTrainingInvoice(Invoice invoice){ 
return invoice.getName().endsWith("Training"); 
} 
List<Invoice> oracleInvoices = 
filterInvoices(invoices, this::isOracleInvoice); 
List<Invoice> trainingInvoices = 
filterInvoices(invoices, this::isTrainingInvoice); 
method references
Step 6: lambdas 
List<Invoice> oracleInvoices = 
filterInvoices(invoices, 
(Invoice invoice) -> invoice.getCustomer() == Customer.ORACLE); 
List<Invoice> trainingInvoices = 
filterInvoices(invoices, 
(Invoice invoice) -> invoice.getName().endsWith(("Training")));
First-class functions 
● Scary functional-programming terminology for a common object-oriented 
pattern (strategy, function object…) 
● All it means is the ability to use a function (method reference, lambda, 
object representing a function) just like a regular value 
○ Pass it as argument to a method 
○ Store it in a variable 
● Lets you cope with requirement changes by representing and passing 
the new requirement as a function
Composing functions 
Functions Composing functions
Composing functions: example 
import java.util.function.Predicate; 
Predicate<Invoice> isFacebookInvoice = this::isFacebookInvoice; 
List<Invoice> facebookAndTraining = 
invoices.stream() 
.filter( isFacebookInvoice.and(this::isTrainingInvoice)) 
.collect(toList()); 
List<Invoice> facebookOrGoogle = 
invoices.stream() 
creating more complex 
functions from building blocks 
.filter( isFacebookInvoice.or(this::isGoogleInvoice)) 
.collect(toList());
Composing functions: why does it work? 
public interface Predicate<T> { 
boolean test(T t); 
default Predicate<T> and(Predicate<? super T> other) { 
Objects.requireNonNull(other); 
return (t) -> test(t) && other.test(t); 
} 
default Predicate<T> or(Predicate<? super T> other) { 
Objects.requireNonNull(other); 
return (t) -> test(t) || other.test(t); 
} 
} 
returns a new 
function that is 
the result of 
composing two 
functions
Creating function pipelines (1) 
public class Letter { 
private final String message; 
public Letter(String message) { 
this.message = message; 
} 
public Letter addDefaultHeader(){ 
return new Letter("From her majesty:n" + message); 
} 
public Letter checkSpelling(){ 
return new Letter(message.replaceAll("FTW", "for the win")); 
} 
public Letter addDefaultFooter(){ 
return new Letter(message + "nKind regards"); 
} 
}
Creating function pipelines (2) 
import java.util.function.Function; 
Function<Letter, Letter> addHeader = Letter::addDefaultHeader; 
Function<Letter, Letter> processingPipeline = 
addHeader.andThen(Letter::checkSpelling) 
.andThen(Letter::addDefaultFooter); 
andThen andThen 
composing 
functions 
addHeader checkSpelling addFooter 
Letter 
{message="Java 
8 FTW!"} 
Letter{message='From 
her majesty: 
Java 8 for the win! 
Kind regards'}
Creating function pipelines (3) 
import java.util.function.Function; 
Function<Letter, Letter> addHeader = Letter::addDefaultHeader; 
Function<Letter, Letter> processingPipeline = 
addHeader.andThen(Letter::addDefaultFooter); 
andThen 
composing 
functions 
addHeader addFooter 
Letter 
{message="Java 
8 FTW!"} 
Letter{message='From 
her majesty: 
Java 8 FTW! 
Kind regards'}
First-class Functions 
Currying 
Immutability 
Optional Data Types 
Conclusions
A conversion function 
// (double, double, double) -> double 
double convert(double amount, double factor, double base) 
{ 
return amount * factor + base; 
} 
// Usage 
double result = convert(10, 1.8, 32); 
assertEquals(result, 50, 0.0);
Currying the conversion function 
// (double, double) -> (double -> double) 
DoubleUnaryOperator convert(double factor, double base) { 
return amount -> amount * factor + base; 
} 
// Partial Application 
DoubleUnaryOperator convertCtoF = convert(1.8, 32); 
// Usage 
double result = convertCtoF.applyAsDouble(10); 
assertEquals(result, 50, 0.0);
More partial application 
// Temperatures 
DoubleUnaryOperator convertCtoF = convert(1.8, 32); 
// Currencies 
DoubleUnaryOperator convert$to£ = convert(0.6, 0); 
// Distance 
DoubleUnaryOperator convertKmToMi = convert(0.62137, 0);
A curry function 
// int -> (int -> int) 
IntFunction<IntUnaryOperator> 
curry(IntBinaryOperator biFunction) { 
return f -> s -> biFunction.applyAsInt(f, s); 
} 
// Usage: 
IntFunction<IntUnaryOperator> add = curry((f, s) -> f + 
s); 
int result = add.apply(1) 
.applyAsInt(2); 
assertEquals(3, result);
Generalised curry function 
// F -> (S -> R) 
<F, S, R> Function<F, Function<S, R>> 
curry(BiFunction<F, S, R> biFunction) { 
return f -> s -> biFunction.apply(f, s); 
}
Summary 
● Currying is about splitting up the arguments of a function. 
● Partial Application is a function “eating” some of its arguments and 
returning a new function 
● You can write your functions in curried form from the beginning or use 
a function to curry them.
First-class Functions 
Currying 
Immutability 
Optional Data Types 
Conclusions
Mutable objects 
public class TrainJourney { 
private int price; 
private TrainJourney onward; 
public TrainJourney(int p, TrainJourney t) { 
price = p; 
onward = t; 
} 
public int getPrice() { 
return price; 
} 
public TrainJourney getOnward() { 
return onward; 
} 
public void setPrice(int price) { 
this.price = price; 
} 
public void setOnward(TrainJourney onward) { 
this.onward = onward; 
} 
}
Immutable objects 
public class TrainJourneyImmutable { 
private final int price; 
private final TrainJourneyImmutable onward; 
public TrainJourneyImmutable(int p, TrainJourneyImmutable t) { 
price = p; 
onward = t; 
} 
public int getPrice() { 
return price; 
} 
public TrainJourneyImmutable getOnward() { 
return onward; 
} 
public TrainJourneyImmutable setPrice(int price) { 
return new TrainJourneyImmutable(price, getOnward()); 
} 
public TrainJourneyImmutable setOnward(TrainJourneyImmutable onward) 
{ 
return new TrainJourneyImmutable(getPrice(), onward); 
} 
}
Mutable approach 
public TrainJourney link(TrainJourney tj1, TrainJourney tj2) { 
if (tj1 == null) { 
return tj2; 
} 
TrainJourney t = tj1; 
while (t.getOnward() != null) { 
t = t.getOnward(); 
} 
t.setOnward(tj2); 
return tj1; 
}
Mutable approach: problem 
TrainJourney linked1 = link(tj1, tj2); 
TrainJourney linked2 = link(tj1, tj2); 
visit(linked2, tj -> { System.out.print(tj.price + " - "); 
}); 
static void visit(TrainJourney journey, Consumer<TrainJourney> c) 
{ 
if (journey != null) { 
c.accept(journey); 
visit(journey.onward, c); 
} 
}
java.lang.StackOverflowError
Immutable approach 
public TrainJourneyImmutable append(TrainJourneyImmutable tj1, 
TrainJourneyImmutable tj2) { 
return tj1 == null ? tj2 
: new TrainJourneyImmutable(tj1.getPrice(), 
append(tj1.getOnward(), tj2) 
); 
}
Related topics 
● Domain Driven Design 
○ Value Classes are Immutable 
● Core Java Improvements 
○ New date & time library in Java 8 has many Immutable Objects 
○ Current Value Types proposal is immutable 
● Tooling 
○ final keyword only bans reassignment 
○ JSR 308 - improved annotation opportunities 
○ Mutability Detector 
○ Findbugs
Immutable objects reduce the scope for bugs.
First-class Functions 
Currying 
Immutability 
Optional Data Types 
Conclusions
Don’t we all love it? 
Exception in thread "main" java.lang.NullPointerException
public String getCarInsuranceName(Person person) { 
return person.getCar().getInsurance().getName(); 
} 
Where’s the NPE?
Defensive checking 
public String getCarInsuranceName(Person person) { 
if (person != null) { 
Car car = person.getCar(); 
if (car != null) { 
Insurance insurance = car.getInsurance(); 
if (insurance != null) { 
return insurance.getName(); 
} 
} 
} 
return "No Insurance"; 
}
Optional 
● Java 8 introduces a new class java.util.Optional<T> 
○ a single-value container 
● Explicit modelling 
○ Immediately clear that its an optional value 
○ better maintainability 
● You need to actively unwrap an Optional 
○ force users to think about the absence case 
○ fewer errors
Updating model 
public class Person { 
private Optional<Car> car; 
public Optional<Car> getCar() { return car; } 
} 
public class Car { 
private Optional<Insurance> insurance; 
public Optional<Insurance> getInsurance() { return insurance; } 
} 
public class Insurance { 
private String name; 
public String getName() { return name; } 
}
Refactoring our example 
public String getCarInsuranceName(Person person) { 
return Optional.ofNullable(person) 
.flatMap(Person::getCar) 
.flatMap(Car::getInsurance) 
.map(Insurance::getName) 
.orElse("No Insurance"); 
}
Consistent use of Optional replaces the use of null
First-class Functions 
Currying 
Immutability 
Optional Data Types 
Conclusions
Summary of benefits 
● First-class functions let you cope for requirement changes 
● Currying lets you re-use code logic 
● Immutability reduces the scope for bugs 
● Optional data types lets you reduce null checking boilerplate and 
prevent bugs
Training Website: http://java8training.com 
http://manning.com/urma http://tinyurl.com/java8lambdas
Any Questions? 
Richard Warburton 
@richardwarburto 
Raoul-Gabriel Urma 
@raoulUK
Example currying use cases 
● Large factory methods 
○ Partially apply some of the arguments and then pass this factory 
object around. 
● Parser Combinators 
○ many(‘a’) - function which parses a, aa, aaa, etc.

Pragmatic functional refactoring with java 8

  • 1.
    Pragmatic Functional Refactoringwith Java 8 Raoul-Gabriel Urma Richard Warburton
  • 4.
    First-class Functions Currying Immutability Optional Data Types Conclusions
  • 5.
    Step 1: filteringinvoices from Oracle public List<Invoice> filterInvoicesFromOracle(List<Invoice> invoices){ List<Invoice> result = new ArrayList<>(); for(Invoice invoice: invoices){ if( invoice.getCustomer() == Customer.ORACLE){ result.add(invoice); } } return result; }
  • 6.
    Step 2a: abstractingthe customer public List<Invoice> filterInvoicesFromCustomer(List<Invoice> invoices, Customer customer){ List<Invoice> result = new ArrayList<>(); for(Invoice invoice: invoices){ if( invoice.getCustomer() == customer){ result.add(invoice); } } return result; }
  • 7.
    Step 2b: abstractingthe name public List<Invoice> filterInvoicesEndingWith(List<Invoice> invoices, String suffix){ List<Invoice> result = new ArrayList<>(); for(Invoice invoice: invoices){ if( invoice.getName().endsWith(suffix)){ result.add(invoice); } } return result; }
  • 8.
    Step 3: messycode-reuse! private List<Invoice> filterInvoicesBad(List<Invoice> invoices, Customer customer, String suffix, boolean flag){ List<Invoice> result = new ArrayList<>(); for(Invoice invoice: invoices){ if(( flag && invoice.getCustomer() == customer) || (!flag && invoice.getName().endsWith(suffix))){ result.add(invoice); } } return result; }
  • 9.
    Step 4a: modelingthe filtering criterion public Predicate<T> { boolean test(T t); } Invoice boolean Predicate<Invoice>
  • 10.
    Step 4b: usingdifferent criterion with objects private List<Invoice> filterInvoices(List<Invoice> invoices, Predicate<Invoice> p){ List<Invoice> result = new ArrayList<>(); for(Invoice invoice: invoices){ if( p.test(invoice)){ result.add(invoice); } } return result; }
  • 11.
    Step 4b: usingdifferent criterion with objects List<Invoice> specificInvoices = filterInvoices(invoices, new FacebookTraining()); private class FacebookTraining implements Predicate<Invoice> { @Override public boolean test(Invoice invoice) { return invoice.getCustomer() == Customer.FACEBOOK && invoice.getName().endsWith("Training"); } }
  • 12.
    Step 5: methodreferences public boolean isOracleInvoice(Invoice invoice){ return invoice.getCustomer() == Customer.ORACLE; } public boolean isTrainingInvoice(Invoice invoice){ return invoice.getName().endsWith("Training"); } List<Invoice> oracleInvoices = filterInvoices(invoices, this::isOracleInvoice); List<Invoice> trainingInvoices = filterInvoices(invoices, this::isTrainingInvoice); method references
  • 13.
    Step 6: lambdas List<Invoice> oracleInvoices = filterInvoices(invoices, (Invoice invoice) -> invoice.getCustomer() == Customer.ORACLE); List<Invoice> trainingInvoices = filterInvoices(invoices, (Invoice invoice) -> invoice.getName().endsWith(("Training")));
  • 14.
    First-class functions ●Scary functional-programming terminology for a common object-oriented pattern (strategy, function object…) ● All it means is the ability to use a function (method reference, lambda, object representing a function) just like a regular value ○ Pass it as argument to a method ○ Store it in a variable ● Lets you cope with requirement changes by representing and passing the new requirement as a function
  • 15.
    Composing functions FunctionsComposing functions
  • 16.
    Composing functions: example import java.util.function.Predicate; Predicate<Invoice> isFacebookInvoice = this::isFacebookInvoice; List<Invoice> facebookAndTraining = invoices.stream() .filter( isFacebookInvoice.and(this::isTrainingInvoice)) .collect(toList()); List<Invoice> facebookOrGoogle = invoices.stream() creating more complex functions from building blocks .filter( isFacebookInvoice.or(this::isGoogleInvoice)) .collect(toList());
  • 17.
    Composing functions: whydoes it work? public interface Predicate<T> { boolean test(T t); default Predicate<T> and(Predicate<? super T> other) { Objects.requireNonNull(other); return (t) -> test(t) && other.test(t); } default Predicate<T> or(Predicate<? super T> other) { Objects.requireNonNull(other); return (t) -> test(t) || other.test(t); } } returns a new function that is the result of composing two functions
  • 18.
    Creating function pipelines(1) public class Letter { private final String message; public Letter(String message) { this.message = message; } public Letter addDefaultHeader(){ return new Letter("From her majesty:n" + message); } public Letter checkSpelling(){ return new Letter(message.replaceAll("FTW", "for the win")); } public Letter addDefaultFooter(){ return new Letter(message + "nKind regards"); } }
  • 19.
    Creating function pipelines(2) import java.util.function.Function; Function<Letter, Letter> addHeader = Letter::addDefaultHeader; Function<Letter, Letter> processingPipeline = addHeader.andThen(Letter::checkSpelling) .andThen(Letter::addDefaultFooter); andThen andThen composing functions addHeader checkSpelling addFooter Letter {message="Java 8 FTW!"} Letter{message='From her majesty: Java 8 for the win! Kind regards'}
  • 20.
    Creating function pipelines(3) import java.util.function.Function; Function<Letter, Letter> addHeader = Letter::addDefaultHeader; Function<Letter, Letter> processingPipeline = addHeader.andThen(Letter::addDefaultFooter); andThen composing functions addHeader addFooter Letter {message="Java 8 FTW!"} Letter{message='From her majesty: Java 8 FTW! Kind regards'}
  • 21.
    First-class Functions Currying Immutability Optional Data Types Conclusions
  • 24.
    A conversion function // (double, double, double) -> double double convert(double amount, double factor, double base) { return amount * factor + base; } // Usage double result = convert(10, 1.8, 32); assertEquals(result, 50, 0.0);
  • 25.
    Currying the conversionfunction // (double, double) -> (double -> double) DoubleUnaryOperator convert(double factor, double base) { return amount -> amount * factor + base; } // Partial Application DoubleUnaryOperator convertCtoF = convert(1.8, 32); // Usage double result = convertCtoF.applyAsDouble(10); assertEquals(result, 50, 0.0);
  • 26.
    More partial application // Temperatures DoubleUnaryOperator convertCtoF = convert(1.8, 32); // Currencies DoubleUnaryOperator convert$to£ = convert(0.6, 0); // Distance DoubleUnaryOperator convertKmToMi = convert(0.62137, 0);
  • 27.
    A curry function // int -> (int -> int) IntFunction<IntUnaryOperator> curry(IntBinaryOperator biFunction) { return f -> s -> biFunction.applyAsInt(f, s); } // Usage: IntFunction<IntUnaryOperator> add = curry((f, s) -> f + s); int result = add.apply(1) .applyAsInt(2); assertEquals(3, result);
  • 28.
    Generalised curry function // F -> (S -> R) <F, S, R> Function<F, Function<S, R>> curry(BiFunction<F, S, R> biFunction) { return f -> s -> biFunction.apply(f, s); }
  • 29.
    Summary ● Curryingis about splitting up the arguments of a function. ● Partial Application is a function “eating” some of its arguments and returning a new function ● You can write your functions in curried form from the beginning or use a function to curry them.
  • 30.
    First-class Functions Currying Immutability Optional Data Types Conclusions
  • 31.
    Mutable objects publicclass TrainJourney { private int price; private TrainJourney onward; public TrainJourney(int p, TrainJourney t) { price = p; onward = t; } public int getPrice() { return price; } public TrainJourney getOnward() { return onward; } public void setPrice(int price) { this.price = price; } public void setOnward(TrainJourney onward) { this.onward = onward; } }
  • 32.
    Immutable objects publicclass TrainJourneyImmutable { private final int price; private final TrainJourneyImmutable onward; public TrainJourneyImmutable(int p, TrainJourneyImmutable t) { price = p; onward = t; } public int getPrice() { return price; } public TrainJourneyImmutable getOnward() { return onward; } public TrainJourneyImmutable setPrice(int price) { return new TrainJourneyImmutable(price, getOnward()); } public TrainJourneyImmutable setOnward(TrainJourneyImmutable onward) { return new TrainJourneyImmutable(getPrice(), onward); } }
  • 33.
    Mutable approach publicTrainJourney link(TrainJourney tj1, TrainJourney tj2) { if (tj1 == null) { return tj2; } TrainJourney t = tj1; while (t.getOnward() != null) { t = t.getOnward(); } t.setOnward(tj2); return tj1; }
  • 35.
    Mutable approach: problem TrainJourney linked1 = link(tj1, tj2); TrainJourney linked2 = link(tj1, tj2); visit(linked2, tj -> { System.out.print(tj.price + " - "); }); static void visit(TrainJourney journey, Consumer<TrainJourney> c) { if (journey != null) { c.accept(journey); visit(journey.onward, c); } }
  • 36.
  • 37.
    Immutable approach publicTrainJourneyImmutable append(TrainJourneyImmutable tj1, TrainJourneyImmutable tj2) { return tj1 == null ? tj2 : new TrainJourneyImmutable(tj1.getPrice(), append(tj1.getOnward(), tj2) ); }
  • 39.
    Related topics ●Domain Driven Design ○ Value Classes are Immutable ● Core Java Improvements ○ New date & time library in Java 8 has many Immutable Objects ○ Current Value Types proposal is immutable ● Tooling ○ final keyword only bans reassignment ○ JSR 308 - improved annotation opportunities ○ Mutability Detector ○ Findbugs
  • 40.
    Immutable objects reducethe scope for bugs.
  • 41.
    First-class Functions Currying Immutability Optional Data Types Conclusions
  • 42.
    Don’t we alllove it? Exception in thread "main" java.lang.NullPointerException
  • 43.
    public String getCarInsuranceName(Personperson) { return person.getCar().getInsurance().getName(); } Where’s the NPE?
  • 44.
    Defensive checking publicString getCarInsuranceName(Person person) { if (person != null) { Car car = person.getCar(); if (car != null) { Insurance insurance = car.getInsurance(); if (insurance != null) { return insurance.getName(); } } } return "No Insurance"; }
  • 45.
    Optional ● Java8 introduces a new class java.util.Optional<T> ○ a single-value container ● Explicit modelling ○ Immediately clear that its an optional value ○ better maintainability ● You need to actively unwrap an Optional ○ force users to think about the absence case ○ fewer errors
  • 46.
    Updating model publicclass Person { private Optional<Car> car; public Optional<Car> getCar() { return car; } } public class Car { private Optional<Insurance> insurance; public Optional<Insurance> getInsurance() { return insurance; } } public class Insurance { private String name; public String getName() { return name; } }
  • 47.
    Refactoring our example public String getCarInsuranceName(Person person) { return Optional.ofNullable(person) .flatMap(Person::getCar) .flatMap(Car::getInsurance) .map(Insurance::getName) .orElse("No Insurance"); }
  • 48.
    Consistent use ofOptional replaces the use of null
  • 49.
    First-class Functions Currying Immutability Optional Data Types Conclusions
  • 50.
    Summary of benefits ● First-class functions let you cope for requirement changes ● Currying lets you re-use code logic ● Immutability reduces the scope for bugs ● Optional data types lets you reduce null checking boilerplate and prevent bugs
  • 52.
    Training Website: http://java8training.com http://manning.com/urma http://tinyurl.com/java8lambdas
  • 53.
    Any Questions? RichardWarburton @richardwarburto Raoul-Gabriel Urma @raoulUK
  • 54.
    Example currying usecases ● Large factory methods ○ Partially apply some of the arguments and then pass this factory object around. ● Parser Combinators ○ many(‘a’) - function which parses a, aa, aaa, etc.