KEMBAR78
Functional aspects of java 8 | PDF
Functional Aspects of Java 8
Shahadat Hossain Chowdhury (Jobaer)
Technical Project Manager
Cefalo AS
Goal of my talk
Things I will talk about
Lamda
Streams
Optional
Lambda
Lambda structure
Parameters -> Body
(Apple a1, Apple a2) ->
a1.getWeight().compareTo(a2.getWeight());
We can use lambda to
parameterize behaviors
A refactoring example
public class Apple {
private String color;
private Integer weight;
public Apple(color, weight) …
public String getColor() …
public void setColor(color) …
public Integer getWeight() …
public void setWeight(weight) …
}
There was an apple store ...
We want to filter all green apples
filterGreenApples(List<Apple> inventory){
List<Apple> result = new ArrayList<>();
for(Apple apple : inventory) {
if("green".equals(apple.getColor())) {
result.add(apple);
}
}
return result;
}
greenApples = filterGreenApples(inventory);
Attempt 1
Filter the “red” ones too!
List<Apple> filterByColor(inventory,color){
List<Apple> result = new ArrayList<>();
for(Apple apple : inventory) {
if(color.equals(apple.getColor())) {
result.add(apple);
}
}
return result;
}
filterByColor(inventory, "green");
filterByColor(inventory, "red");
Attempt 2
And filter also by “weight”
Attempt 3
List<Apple> filterApples(
inventory, color, weight, flag) {
List<Apple> result = new ArrayList<>();
for(Apple apple : inventory) {
if((flag && color.equals(apple.getColor()))
|| (!flag && apple.getWeight() >= weight)) {
result.add(apple);
}
}
return result;
}
filterApples(inventory, "green", 0, true);
filterApples(inventory, "red", 0, true);
filterApples(inventory, "", 150, false);
public interface ApplePredicate {
boolean test(Apple apple);
}
class GreenPredicate implements … {
@Override
public boolean test(Apple apple) {
return "green".equals(apple.getColor());
}
}
Attempt 4
HeavyPredicate implements ApplePredicate {
public boolean test(Apple apple) {
return apple.getWeight() >= 150;
}
}
RedAndHeavyPredicate implements … {
public boolean test(Apple apple) {
return "red".equals(apple.getColor())
&& apple.getWeight() >= 150;
}
}
Attempt 4 ...
filterApples(
inventory, ApplePredicate predicate) {
List<Apple> result = new ArrayList<>();
for(Apple apple : inventory) {
if(predicate.test(apple)) {
result.add(apple);
}
}
return result;
}
filterApples(inventory, new GreenPredicate());
filterApples(inventory, new AppleHeavyPredicate());
filterApples(inventory, new RedAndHeavyPredicate());
Attempt 4 ...
List<Apple> inventory = getInventory();
greenApples = filterApples(
inventory, new ApplePredicate() {
public boolean test(Apple apple) {
return "red".equals(apple.getColor());
}
});
Attempt 5 … anon class
greenApples = filterApples(
inventory,
apple -> "red".equals(apple.getColor()));
heavyApples = filterApples(
inventory,
apple -> apple.getWeight() >= 150);
Attempt 6 .. Power of lambda
public interface Predicate<T> {
boolean test(T t);
}
List<T> filter(List<T> list,
Predicate<T> p) {
List<T> result = new ArrayList<>();
for (T e : list) {
if (p.test(e)) {
result.add(e);
}
}
return result;
}
Final attempt .. more generic
greenApples = filter(
inventory, (Apple a) ->
"green".equals(a.getColor()));
heavyApples = filter(
inventory,
(Apple a) -> a.getWeight() >= 150);
numbers = Arrays.asList(1,2,3,4,5,6,7,8);
evenNumbers = filter(
numbers, (Integer i) -> i % 2 == 0);
Final attempt .. more generic
Lambda in a nutshell
● Anonymous
● Function
● Passed around
● Concise
Misc. lambda use
Event handling
Label l = new Label("...");
Button b = new Button();
b.addActionListener(new ActionListener() {
@Override
public void actionPerformed (
ActionEvent e) {
l.setText("Sending...");
}
});
Without lambda
b.addActionListener(
(ActionEvent e) -> l.setText("Sending.. ")
);
Using lambda
Implement runnables
Thread regular = new Thread( new Runnable()
{
@Override
public void run() {
System.out.println("From runnable");
}
});
Traditional way
Thread lambda = new Thread(
() -> System.out.println("From lambda"));
With lambda
Using in comparators
List<Apple> inventory = getInventory();
inventory.sort(new AppleComparator());
class AppleComparator implements
Comparator<Apple> {
@Override
public int compare(Apple a1, Apple a2) {
return a1.getWeight().compareTo(
a2.getWeight());
}
}
Sort with named comparator
List<Apple> inventory = getInventory();
inventory.sort(new Comparator<Apple>() {
@Override
public int compare(Apple a1, Apple a2) {
return a1.getWeight().compareTo(
a2.getWeight());
}
});
Sort with Anon. comparator
List<Apple> inventory = getInventory();
inventory.sort(
(a1, a2) ->
a1.getWeight().compareTo(
a2.getWeight()));
Sort with Lambda
List<Apple> inventory = getInventory();
inventory.sort(
comparing(Apple::getWeight));
Sort with method reference
Composing lambdas/functions
List<Apple> inventory = getInventory();
inventory.sort(comparing(Apple::getWeight);
inventory.sort(
comparing(Apple::getWeight).reversed());
inventory.sort(
comparing(Apple::getColor).
thenComparing(Apple::getWeight));
Compose comparators
List<Apple> inventory = getInventory();
Predicate<Apple> redApple =
apple -> "red".equals(apple.getColor());
Predicate<Apple> notRedApple =
redApple.negate();
inventory.stream().filter(notRedApple);
Compose Predicates …
Compose Predicates
Predicate<Apple> redAndHeavy =
redApple.and(apple ->
apple.getWeight() > 150);
redApple
.and(a -> a.getWeight() > 150)
.or(a -> "green".equals(a.getColor()));
Compose Functions
Function<...> inc = a -> a + 1;
Function<...> dub = a -> a * 2;
Function<..> incThenDub = inc.andThen(dub);
incThenDub.apply(9) // returns 20
Function<..> incOfDub = inc.compose(dub);
incOfDub.apply(9) // returns 19
But where it can be used?
class Letter {
static String addHeader(String text) {
return "From Bob: " + text;
}
static String addFooter(String text) {
return text + "nKind regards";
}
static String checkSpelling(String text) {
return
text.replaceAll("labda", "lambda");
}
}
Compose Functions
Function<..> addHeader = Letter::addHeader;
Function<..> pipeline1 =
addHeader
.andThen(Letter::checkSpelling)
.andThen(Letter::addFooter);
Function<..> pipeline2 =
addHeader
.andThen(Letter::addFooter);
There’s more about lambda, but I
am not covering here
Streams
Let’s start with an example
Domain class
public class Dish {
Dish(String name, int calories) ..
public String getName() ...
public int getCalories() ...
...
}
Get the low calorie dish names,
sorted by calorie
Java 7 approach ...
//First we get all the low cal dishes
List<Dish> lowCals = new ArrayList<Dish>();
for (Dish dish : menu) {
if(dish.getCalories() < 400){
lowCals.add(dish);
}
}
Java 7 approach ...
// Sort the dishes according to calorie
Collections.sort(lowCals, new
Comparator<Dish>() {
public int compare(Dish d1, Dish d2) {
return Integer.compare
(d1.getCalories(), d2.getCalories());
}
});
// And now get the names as result
List<String> lowCalNames = new ArrayList<>();
for (Dish lowCal : lowCals) {
lowCalNames.add(lowCal.getName());
}
Come to Java 8
List<String> newLowCalNames =
menu.stream()
.filter(d -> d.getCalories() < 400)
.sorted(comparing(Dish::getCalories))
.map(Dish::getName)
.collect(toList());
Difference with Collection
Collection Streams
Traversable only once.
Traversing again will throw
IllegalStageException!
External iterator vs. internal
iterator
//External iteration
List<String> names = new ArrayList<>();
for(Dish d : menu) {
names.add(d.getName());
}
//Internal iteration
List<String> names = menu.stream()
.map(Dish::getName).collect(toList());
Stream operations
● Intermediate Operations
○ filter, map, limit, sorted,
distinct, skip etc.
● Terminal operations
○ forEach, count, collect, reduce
Reducing ...
Integer sum(List<Integer> numbers ) {
int result = 0;
for (Integer number : numbers) {
result = result + number;
}
return result;
}
Integer product(List<Integer> numbers) {
int result = 1;
for(Integer num : numbers) {
result = result * num;
}
return result;
}
Reducing
Integer sumByReduce =
ints.stream()
.reduce(0, (a, b) -> a + b);
Integer prodByReduce =
ints.stream()
.reduce(1, (a, b) -> a * b);
Streams api let’s you write code
that is ..
● Declarative
● Composable
● Parallelizable
I just touched the surface.
Have a look a the full api
http://docs.oracle.com/javase/8/docs/api/java/util/stream/package
-summary.html
Optional<T>
Tony Hoare
He developed the Quicksort
Algorithm
He also invented another thing …
Null reference
What’s wrong with this method?
String getCarInsuranceName(Person person){
return person.getCar()
.getInsurance()
.getName();
}
It may fail spectacularly with a
NullPointerException!
So we need to avoid nulls at any
cost!
Avoiding nulls - attempt 1
String getCarInsuranceName(Person person) {
if(person != null){
Car car = person.getCar();
if(car != null) {
Insurance ins = car.getInsurance();
if(ins != null){
return ins.getName();
}
}
}
return “Unknown”;
}
Avoiding nulls - early exit
String getCarInsuranceName(Person person) {
if(person == null) {
return “Unknown”;
}
Car car = person.getCar();
if(car == null){
return “Unknown”;
}
Insurance ins = car.getInsurance();
if(ins == null){
return “Unknown”;
}
return ins.getName();
}
Problems with Null
● Source of error
● Bloats your code
● Breaks java philosophy
● Hole in the type system
Meet Optional
Optional<Car> optCar = Optional.empty();
Optional<Car> optCar = Optional.of(car);
Optional<Car> optCar =
Optional.ofNullable(car);
Working with Optional
Optional<Insurance> optInsurance =
Optional.ofNullable(insurance);
Optional<String> name =
optInsurance.map(Insurance::getName);
String value = name.orElse(“Unknown”);
Let’s refactor our previous code
Car getCar() .. // previous
Optional<Car> getCar() … // now
Insurance getInsurace() … // previous
Optional<Insurance> getInsurance() … // now
and so on ..
And then we may try ...
Optional<Person>
optPerson = Optional.ofNullable(person);
Optional<String> name = optPerson
.map(Person::getCar)
.map(Car::getInsurance)
.map(Insurance::getName);
But that doesn’t work ...
Optional<Person> optPerson =
Optional.of(person);
optPerson.map(Person::getCar)
//returns Optional<Optional<Car>>
Mighty ‘flatMap’ to the rescue
This is what ‘map’ does
person.flatMap(Person::getCar)
will return a Optional<Car>
instead of
Optional<Optional<Car>>
This is what ‘flatMap’ does
Power of Optional and flatMap
String getCarInsuranceName(
Optional<Person> person) {
return
person.flatMap(Person::getCar)
.flatMap(Car::getInsurance)
.map(Insurance::getName)
.orElse(“Unknown”);
}
There are some other features
which isn’t covered here
So in general functional
programming lets us write code
that is ...
Power of Optional and flatMap
String getCarInsuranceName(
Optional<Person> person) {
return
person.flatMap(Person::getCar)
.flatMap(Car::getInsurance)
.map(Insurance::getName)
.orElse(“Unknown”);
}
Before I finish ...
Keep learning!
Learn some other functional
languages
Some books
Some MOOCs
https://www.edx.org/course/introduction-functional-programming-delftx-fp101x
https://www.coursera.org/course/progfun
https://www.coursera.org/course/reactive
Don’t rush!
Please don’t!
Thank you very much!
Example code on github.
https://github.com/JobaerChowdhury/fpinjava
Follow me on twitter
@JobaerChowdhury
Find me on linkedin
http://bd.linkedin.com/in/jobaer
Questions?

Functional aspects of java 8

  • 1.
    Functional Aspects ofJava 8 Shahadat Hossain Chowdhury (Jobaer) Technical Project Manager Cefalo AS
  • 2.
  • 3.
    Things I willtalk about Lamda Streams Optional
  • 4.
  • 5.
    Lambda structure Parameters ->Body (Apple a1, Apple a2) -> a1.getWeight().compareTo(a2.getWeight());
  • 6.
    We can uselambda to parameterize behaviors
  • 7.
  • 8.
    public class Apple{ private String color; private Integer weight; public Apple(color, weight) … public String getColor() … public void setColor(color) … public Integer getWeight() … public void setWeight(weight) … } There was an apple store ...
  • 9.
    We want tofilter all green apples
  • 10.
    filterGreenApples(List<Apple> inventory){ List<Apple> result= new ArrayList<>(); for(Apple apple : inventory) { if("green".equals(apple.getColor())) { result.add(apple); } } return result; } greenApples = filterGreenApples(inventory); Attempt 1
  • 11.
  • 12.
    List<Apple> filterByColor(inventory,color){ List<Apple> result= new ArrayList<>(); for(Apple apple : inventory) { if(color.equals(apple.getColor())) { result.add(apple); } } return result; } filterByColor(inventory, "green"); filterByColor(inventory, "red"); Attempt 2
  • 13.
    And filter alsoby “weight”
  • 14.
    Attempt 3 List<Apple> filterApples( inventory,color, weight, flag) { List<Apple> result = new ArrayList<>(); for(Apple apple : inventory) { if((flag && color.equals(apple.getColor())) || (!flag && apple.getWeight() >= weight)) { result.add(apple); } } return result; } filterApples(inventory, "green", 0, true); filterApples(inventory, "red", 0, true); filterApples(inventory, "", 150, false);
  • 15.
    public interface ApplePredicate{ boolean test(Apple apple); } class GreenPredicate implements … { @Override public boolean test(Apple apple) { return "green".equals(apple.getColor()); } } Attempt 4
  • 16.
    HeavyPredicate implements ApplePredicate{ public boolean test(Apple apple) { return apple.getWeight() >= 150; } } RedAndHeavyPredicate implements … { public boolean test(Apple apple) { return "red".equals(apple.getColor()) && apple.getWeight() >= 150; } } Attempt 4 ...
  • 17.
    filterApples( inventory, ApplePredicate predicate){ List<Apple> result = new ArrayList<>(); for(Apple apple : inventory) { if(predicate.test(apple)) { result.add(apple); } } return result; } filterApples(inventory, new GreenPredicate()); filterApples(inventory, new AppleHeavyPredicate()); filterApples(inventory, new RedAndHeavyPredicate()); Attempt 4 ...
  • 18.
    List<Apple> inventory =getInventory(); greenApples = filterApples( inventory, new ApplePredicate() { public boolean test(Apple apple) { return "red".equals(apple.getColor()); } }); Attempt 5 … anon class
  • 19.
    greenApples = filterApples( inventory, apple-> "red".equals(apple.getColor())); heavyApples = filterApples( inventory, apple -> apple.getWeight() >= 150); Attempt 6 .. Power of lambda
  • 20.
    public interface Predicate<T>{ boolean test(T t); } List<T> filter(List<T> list, Predicate<T> p) { List<T> result = new ArrayList<>(); for (T e : list) { if (p.test(e)) { result.add(e); } } return result; } Final attempt .. more generic
  • 21.
    greenApples = filter( inventory,(Apple a) -> "green".equals(a.getColor())); heavyApples = filter( inventory, (Apple a) -> a.getWeight() >= 150); numbers = Arrays.asList(1,2,3,4,5,6,7,8); evenNumbers = filter( numbers, (Integer i) -> i % 2 == 0); Final attempt .. more generic
  • 22.
    Lambda in anutshell ● Anonymous ● Function ● Passed around ● Concise
  • 23.
  • 24.
  • 25.
    Label l =new Label("..."); Button b = new Button(); b.addActionListener(new ActionListener() { @Override public void actionPerformed ( ActionEvent e) { l.setText("Sending..."); } }); Without lambda
  • 26.
    b.addActionListener( (ActionEvent e) ->l.setText("Sending.. ") ); Using lambda
  • 27.
  • 28.
    Thread regular =new Thread( new Runnable() { @Override public void run() { System.out.println("From runnable"); } }); Traditional way
  • 29.
    Thread lambda =new Thread( () -> System.out.println("From lambda")); With lambda
  • 30.
  • 31.
    List<Apple> inventory =getInventory(); inventory.sort(new AppleComparator()); class AppleComparator implements Comparator<Apple> { @Override public int compare(Apple a1, Apple a2) { return a1.getWeight().compareTo( a2.getWeight()); } } Sort with named comparator
  • 32.
    List<Apple> inventory =getInventory(); inventory.sort(new Comparator<Apple>() { @Override public int compare(Apple a1, Apple a2) { return a1.getWeight().compareTo( a2.getWeight()); } }); Sort with Anon. comparator
  • 33.
    List<Apple> inventory =getInventory(); inventory.sort( (a1, a2) -> a1.getWeight().compareTo( a2.getWeight())); Sort with Lambda
  • 34.
    List<Apple> inventory =getInventory(); inventory.sort( comparing(Apple::getWeight)); Sort with method reference
  • 35.
  • 36.
    List<Apple> inventory =getInventory(); inventory.sort(comparing(Apple::getWeight); inventory.sort( comparing(Apple::getWeight).reversed()); inventory.sort( comparing(Apple::getColor). thenComparing(Apple::getWeight)); Compose comparators
  • 37.
    List<Apple> inventory =getInventory(); Predicate<Apple> redApple = apple -> "red".equals(apple.getColor()); Predicate<Apple> notRedApple = redApple.negate(); inventory.stream().filter(notRedApple); Compose Predicates …
  • 38.
    Compose Predicates Predicate<Apple> redAndHeavy= redApple.and(apple -> apple.getWeight() > 150); redApple .and(a -> a.getWeight() > 150) .or(a -> "green".equals(a.getColor()));
  • 39.
    Compose Functions Function<...> inc= a -> a + 1; Function<...> dub = a -> a * 2; Function<..> incThenDub = inc.andThen(dub); incThenDub.apply(9) // returns 20 Function<..> incOfDub = inc.compose(dub); incOfDub.apply(9) // returns 19
  • 40.
    But where itcan be used? class Letter { static String addHeader(String text) { return "From Bob: " + text; } static String addFooter(String text) { return text + "nKind regards"; } static String checkSpelling(String text) { return text.replaceAll("labda", "lambda"); } }
  • 41.
    Compose Functions Function<..> addHeader= Letter::addHeader; Function<..> pipeline1 = addHeader .andThen(Letter::checkSpelling) .andThen(Letter::addFooter); Function<..> pipeline2 = addHeader .andThen(Letter::addFooter);
  • 42.
    There’s more aboutlambda, but I am not covering here
  • 43.
  • 44.
  • 45.
    Domain class public classDish { Dish(String name, int calories) .. public String getName() ... public int getCalories() ... ... }
  • 46.
    Get the lowcalorie dish names, sorted by calorie
  • 47.
    Java 7 approach... //First we get all the low cal dishes List<Dish> lowCals = new ArrayList<Dish>(); for (Dish dish : menu) { if(dish.getCalories() < 400){ lowCals.add(dish); } }
  • 48.
    Java 7 approach... // Sort the dishes according to calorie Collections.sort(lowCals, new Comparator<Dish>() { public int compare(Dish d1, Dish d2) { return Integer.compare (d1.getCalories(), d2.getCalories()); } }); // And now get the names as result List<String> lowCalNames = new ArrayList<>(); for (Dish lowCal : lowCals) { lowCalNames.add(lowCal.getName()); }
  • 49.
    Come to Java8 List<String> newLowCalNames = menu.stream() .filter(d -> d.getCalories() < 400) .sorted(comparing(Dish::getCalories)) .map(Dish::getName) .collect(toList());
  • 50.
  • 51.
  • 52.
    Traversable only once. Traversingagain will throw IllegalStageException!
  • 53.
    External iterator vs.internal iterator //External iteration List<String> names = new ArrayList<>(); for(Dish d : menu) { names.add(d.getName()); } //Internal iteration List<String> names = menu.stream() .map(Dish::getName).collect(toList());
  • 54.
    Stream operations ● IntermediateOperations ○ filter, map, limit, sorted, distinct, skip etc. ● Terminal operations ○ forEach, count, collect, reduce
  • 55.
    Reducing ... Integer sum(List<Integer>numbers ) { int result = 0; for (Integer number : numbers) { result = result + number; } return result; } Integer product(List<Integer> numbers) { int result = 1; for(Integer num : numbers) { result = result * num; } return result; }
  • 56.
    Reducing Integer sumByReduce = ints.stream() .reduce(0,(a, b) -> a + b); Integer prodByReduce = ints.stream() .reduce(1, (a, b) -> a * b);
  • 57.
    Streams api let’syou write code that is .. ● Declarative ● Composable ● Parallelizable
  • 58.
    I just touchedthe surface. Have a look a the full api http://docs.oracle.com/javase/8/docs/api/java/util/stream/package -summary.html
  • 59.
  • 60.
  • 61.
    He developed theQuicksort Algorithm
  • 62.
    He also inventedanother thing …
  • 63.
  • 64.
    What’s wrong withthis method? String getCarInsuranceName(Person person){ return person.getCar() .getInsurance() .getName(); }
  • 65.
    It may failspectacularly with a NullPointerException!
  • 66.
    So we needto avoid nulls at any cost!
  • 67.
    Avoiding nulls -attempt 1 String getCarInsuranceName(Person person) { if(person != null){ Car car = person.getCar(); if(car != null) { Insurance ins = car.getInsurance(); if(ins != null){ return ins.getName(); } } } return “Unknown”; }
  • 68.
    Avoiding nulls -early exit String getCarInsuranceName(Person person) { if(person == null) { return “Unknown”; } Car car = person.getCar(); if(car == null){ return “Unknown”; } Insurance ins = car.getInsurance(); if(ins == null){ return “Unknown”; } return ins.getName(); }
  • 69.
    Problems with Null ●Source of error ● Bloats your code ● Breaks java philosophy ● Hole in the type system
  • 70.
    Meet Optional Optional<Car> optCar= Optional.empty(); Optional<Car> optCar = Optional.of(car); Optional<Car> optCar = Optional.ofNullable(car);
  • 71.
    Working with Optional Optional<Insurance>optInsurance = Optional.ofNullable(insurance); Optional<String> name = optInsurance.map(Insurance::getName); String value = name.orElse(“Unknown”);
  • 72.
    Let’s refactor ourprevious code Car getCar() .. // previous Optional<Car> getCar() … // now Insurance getInsurace() … // previous Optional<Insurance> getInsurance() … // now and so on ..
  • 73.
    And then wemay try ... Optional<Person> optPerson = Optional.ofNullable(person); Optional<String> name = optPerson .map(Person::getCar) .map(Car::getInsurance) .map(Insurance::getName);
  • 74.
    But that doesn’twork ... Optional<Person> optPerson = Optional.of(person); optPerson.map(Person::getCar) //returns Optional<Optional<Car>>
  • 75.
  • 76.
    This is what‘map’ does
  • 77.
    person.flatMap(Person::getCar) will return aOptional<Car> instead of Optional<Optional<Car>> This is what ‘flatMap’ does
  • 78.
    Power of Optionaland flatMap String getCarInsuranceName( Optional<Person> person) { return person.flatMap(Person::getCar) .flatMap(Car::getInsurance) .map(Insurance::getName) .orElse(“Unknown”); }
  • 79.
    There are someother features which isn’t covered here
  • 80.
    So in generalfunctional programming lets us write code that is ...
  • 81.
    Power of Optionaland flatMap String getCarInsuranceName( Optional<Person> person) { return person.flatMap(Person::getCar) .flatMap(Car::getInsurance) .map(Insurance::getName) .orElse(“Unknown”); }
  • 82.
  • 83.
  • 84.
    Learn some otherfunctional languages
  • 85.
  • 88.
  • 89.
  • 90.
  • 91.
  • 92.
  • 93.
  • 94.
    Thank you verymuch! Example code on github. https://github.com/JobaerChowdhury/fpinjava Follow me on twitter @JobaerChowdhury Find me on linkedin http://bd.linkedin.com/in/jobaer
  • 95.