KEMBAR78
Writing better functional java code - devnexus | PDF
Writing better
Functional Java code@BrianVerm
@BrianVerm
@BrianVerm
@BrianVerm
@BrianVerm
Brian Vermeer
Developer Advocate
Don’t do to much in a single lambda
@BrianVerm
Lambda Expression
In computer programming, a lambda expression is a function definition
that is not bound to an identifier. Anonymous functions are often:[1]
• arguments being passed to higher-order functions, or
• used for constructing the result of a higher-order function that
needs to return a function.
@BrianVerm
Lambda in Java
Anonymous Inner functions
Satisfy a Functional Interface
They only exist in runtime
Single Line <input> -> <output> or Block <input> -> { function body }
@BrianVerm
Example
@BrianVerm
public void execute() {
Beer grolsch = new Beer("Grolsch", 4.3);
String result = handleBeer(grolsch,
beer -> grolsch.getName() + "-" + grolsch.getAlcohol());
System.out.println(result);
}
private String handleBeer(Beer beer, Function<Beer, String> func) {
System.out.println("handling beer " + beer.getName());
return func.apply(beer);
}
//Output : Grolsch-4.3
Example
@BrianVerm
public void execute() {
Beer grolsch = new Beer("Grolsch", 4.3);
String result = handleBeer(grolsch,
beer -> grolsch.getName() + "-" + grolsch.getAlcohol());
System.out.println(result);
}
private String handleBeer(Beer beer, Function<Beer, String> func) {
System.out.println("handling beer " + beer.getName());
return func.apply(beer);
}
//Output : Grolsch-4.3
Don’t do Block Lambda
@BrianVerm
beer -> {
String name;
if (beer.getName().contains(" ")) {
name = beer.getName().replace(" ", "");
} else {
name = beer.getName();
}
try {
name += Integer.parseInt(beer.getAlcoholPrecentage().toString());
} catch (NumberFormatException nfe) {
name += beer.getAlcoholPrecentage();
}
return name;
}
Transform to a methode
@BrianVerm
private String createFullName (Beer beer){
String name;
if (beer.getName().contains(" ")) {
name = beer.getName().replace(" ", "");
} else {
name = beer.getName();
}
try {
name += Integer.parseInt(beer.getAlcoholPrecentage().toString());
} catch (NumberFormatException nfe) {
name += beer.getAlcoholPrecentage();
}
return name;
}
String result = handleBeer(grolsch, this::createFullName);
Returning a Stream
@BrianVerm
A stream is NOT a data
structure
@BrianVerm
A stream is NOT a data
structure
@BrianVerm
A stream is NOT a data
structure
What is Stream ( in Java)
Flow of data derived from a Collection
Can create a pipeline of function that can be evaluated
Intermediate result
Lazy evaluated by nature
Can transform data, cannot mutate data
@BrianVerm
JAVA Streams
@BrianVerm
stringLists.stream()

.map(str -> str.toUpperCase())

.collect(Collectors.toList());
Intermediate
filter 

distinct

map 

flatMap

Terminal
reduce

collect

toArray

count

max

min

limit 

skip

sorted

peek
findAny

findFirst

forEach

allMatch

anyMatch
List<Beer> beers = getBeers();

Stream<Beer> beerStream = beers.stream();



beerStream.forEach(b ->System.out.println(b.getName())); //1


beerStream.forEach(b ->System.out.println(b.getAlcohol())); //2
Only use a Stream once
Line 2 will give: 



java.lang.IllegalStateException: stream has already been operated upon or
closed
@BrianVerm
public Stream<Beer> getMeMyBeers()
public void execute() {
getMeMyBeers() //don’t know if it is consumed yet!!!!
…
}
Be careful with returning a Stream
@BrianVerm
Returning a Stream
Private intermediate function
When new stream is created every time
When result is very large or might be infinite
By default return a collection
@BrianVerm
Mutable Object
@BrianVerm
Functional Programming
In computer science, functional programming is a programming
paradigma style of building the structure and elements of computer
programs that treats computation as the evaluation of mathematical
functions and avoids changing-state and mutable data.
It is a declarative programming paradigm, which means programming is
done with expressions or declarations instead of statements.
— wikipedia
@BrianVerm
@BrianVerm
@BrianVerm
– Kevlin Henney
“Asking a question should not
change the answer, nor
should asking it twice”
Immutable objects
Less moving parts
Easier to reason about code
No need to keep a mental map of the state an object is in.
@BrianVerm
Stream cannot mutate
@BrianVerm
private final List<Beer> beers = List.of(new Beer("Heineken", 5.2),
new Beer("Amstel", 5.1));
public void execute() {
List<Beer> beersNew = beers.stream()
.map(beer -> beer.setName(“foo”)) //not allowed
.collect(Collectors.toList());
}
Stream should not mutate !
@BrianVerm
private class Beer {
String name;
Double alcohol;
public Beer setName(String name){
this.name = name;
return this;
}
}
private final List<Beer> beers = List.of(new Beer("Heineken", 5.2), new Beer("Amstel",
5.1));
public void execute() {
List<Beer> beersNew = beers.stream()
.map(beer -> beer.setName("foo"))
.collect(Collectors.toList());
System.out.println(beers);
System.out.println(beersNew);
}
Stream should not mutate !
@BrianVerm
private class Beer {
String name;
Double alcohol;
public Beer setName(String name){
this.name = name;
return this;
}
}
private final List<Beer> beers = List.of(new Beer("Heineken", 5.2), new Beer("Amstel",
5.1));
public void execute() {
List<Beer> beersNew = beers.stream()
.map(beer -> beer.setName("foo"))
.collect(Collectors.toList());
System.out.println(beers);
System.out.println(beersNew);
}
Return a new copy
@BrianVerm
private class Beer {
String name;
Double alcohol;
public Beer withName(String name){
return new Beer(name, this.alcohol);
}
}
private final List<Beer> beers = List.of(new Beer("Heineken", 5.2), new Beer("Amstel",
5.1));
public void execute() {
List<Beer> beersNew = beers.stream()
.map(beer -> beer.withName("foo"))
.collect(Collectors.toList());
System.out.println(beers);
System.out.println(beersNew);
}
Overusing forEach
@BrianVerm
forEach()
Terminal functional on a stream.
Takes a consumer.
Can be used to apply side effects.
for-loop without the external iterator
@BrianVerm
simple forEach example
@BrianVerm
List<String> names = List.of("James", "Trisha", "Joshua",
"Jessica","Simon", “Heather”, “Roberto”);
names.stream()
.map(String::toUpperCase)
.forEach(System.out::println);
mutation with forEach
@BrianVerm
List<Beer> beers = List.of(new Beer("Heineken", 5.2), new
Beer("Amstel", 5.1));
beers.stream()
.forEach(beer -> beer.setAlcohol(0.0));
overusing forEach
@BrianVerm
private class Beer {
String name;
Double alcohol;
List<String> reviews;
Integer rating;
}
List<Beer> beers = List.of(new Beer("Heineken", 5.2), new Beer("Amstel", 5.1));
//enrich with ratings
beers.stream().forEach(beer -> beer.setRating(findRating(beer.getName())));
//enrich with reviews
beers.stream().forEach(beer -> beer.setReviews(findReviews(beer.getName())));
…
Order of operations
@BrianVerm
Assignment
Beers -> Brewer -> Country
I want the first 3 unique brewers countries from the beer library as comma separated String
@BrianVerm
beerLib.stream()
.map(Beer::getBrewer)
.distinct()
.limit(3)
.map(Brewer::getCountry)
.map(String::toUpperCase)
.collect(Collectors.joining(“,”));
Assignment
Beers -> Brewer -> Country
I want the first 3 unique brewers countries from the beer library as comma separated String
@BrianVerm
beerLib.stream()
.map(Beer::getBrewer)
.distinct()
.limit(3)
.map(Brewer::getCountry)
.map(String::toUpperCase)
.collect(Collectors.joining(“,”));
// wrong
Assignment
Beers -> Brewer -> Country
I want the first 3 unique brewers countries from the beer library as comma separated String
@BrianVerm
beerLib.stream()
.map(Beer::getBrewer)
.map(Brewer::getCountry)
.map(String::toUpperCase)
.distinct()
.limit(3)
.collect(Collectors.joining(“,”));
// correct
Infinite stream
@BrianVerm
IntStream.iterate(0, i -> ( i + 1) % 2)

.distinct()

.limit(10)

.forEach(i -> System.out.println(i));
Infinite stream
@BrianVerm
IntStream.iterate(0, i -> ( i + 1) % 2)

.distinct()

.limit(10)

.forEach(i -> System.out.println(i));

// will run forever
IntStream.iterate(0, i -> ( i + 1) % 2)

.limit(10)

.distinct()

.forEach(i -> System.out.println(i));
//will terminate
Infinite stream
@BrianVerm
IntStream.iterate(0, i -> ( i + 1) % 2)

.parallel()
.distinct()

.limit(10)

.forEach(i -> System.out.println(i));

// will run forever on all threads.
Solution
1. Look closely at the order of operations

- prevent incorrect answers



2. Only use infinite streams when absolutely necessary.
rather use: 

@BrianVerm
IntStream.range(0,10);
IntStream.rangeClosed(0,10);
IntStream.iterate(0, i -> i < 10, i -> i + 1); //java 9 and up
Create higher-order functions
@BrianVerm
Higher-Order function
Taking a function as a parameter of a method
Insert behaviour instead of just data
A new level of abstraction
Reduce boilerplate / duplicate code
@BrianVerm
Duplicate code
@BrianVerm
private void logUpper(String str) {

//super interesting code

System.out.println(str.toUpperCase());

//even more interesting code

}



private void logLower(String str) {

//super interesting code

System.out.println(str.toLowerCase());

//even more interesting code

}
Insert behaviour
@BrianVerm
private void logUpper(String string) {

doSuperCoolStuff(string, s -> s.toUpperCase());

}



private void logLower(String string) {

doSuperCoolStuff(string, s -> s.toLowerCase());

}



private void doFoo(String str, Function<String, String> func) {

//super interesting code

System.out.println(func.apply(str));

//even more interesting code

}
Real situation (Webshop Basket)
MongoDB primary (read/write), multiple secondary (read)
Writing only on the primary
Mutating a basket:
Read from Primary

Edit

Run rule engine

Save to Primary

@BrianVerm
Real situation (Webshop Basket)
MongoDB primary (read/write), multiple secondary (read)
Writing only on the primary
Mutating a basket:
Read from Primary

Edit

Run rule engine

Save to Primary

@BrianVerm
Mutator
@BrianVerm
public void mutatorById(String id, Consumer<Basket> action) {
Basket basket = repoPrimary.findById(id);
action.accept(basket);
RuleEngine.run(basket)
repoPrimary.save(basket);
}
Mutator
@BrianVerm
public void mutatorById(String id, Consumer<Basket> action) {
Basket basket = repoPrimary.findById(id);
action.accept(basket);
RuleEngine.run(basket)
repoPrimary.save(basket);
}
public void addItem(String uuid, Item item) {
mutatorById(uuid, basket -> basket.addItem(item));
}
public void removeItem(String uuid, Item item) {
mutatorById(uuid, basket -> basket.removeItem(item));
}
Getting an Optional
@BrianVerm
Optional
Java’s implementation of the Maybe Monad
Encapsulation to handle possible null value
Consider it a wrapper where a value can be absent
Force the user to unpack the Optional before using it.
@BrianVerm
Optional
@BrianVerm
Optional<String> c = null //please avoid this
public void execute() {
Optional<String> maybeString = getText();
String unpacked = maybeString.get();
}
Unpack Optional
@BrianVerm
public void execute() {
Optional<String> maybeString = getText();
String unpacked = maybeString.get();
}
Unpack Optional
@BrianVerm
NoSuchElementException
public void execute() {
Optional<String> maybeString = getText();
String unpacked = maybeString.get();
}
Unpack Optional
@BrianVerm
public void execute() {
Optional<String> maybeString = getText();
if (maybeString.isPresent()) {
String unpacked = maybeString.get();
}
}
NoSuchElementException
Unpack Optional
@BrianVerm
public void execute() {
Optional<String> maybeString = getText();
maybeString.ifPresent( str -> /* doSomething */ );
}
public void execute() {
Optional<String> maybeString = getText();
maybeString.map(str -> str + “.");
}
What else ..??
@BrianVerm
Alternative flow
- orElse()
- orElseGet()
- orElseThrow()
@BrianVerm
orElseThrow
@BrianVerm
public void execute() {

Optional<String> maybeString = Optional.empty();

maybeString

.map(this::runIfExist)

.orElseThrow(() -> new RuntimeException("Optional was empty"));

}
orElse
@BrianVerm
public void execute() {

Optional<String> maybeString = Optional.of("foo");

String newString = maybeString

.map(this::runIfExist)

.orElse(runIfEmpty());

System.out.println(newString);

}

private String runIfExist(String str) {

System.out.println("only run if optional is filled ");

return str;

}

private String runIfEmpty() {

System.out.println("only run if empty");

return "empty";

}
orElse
@BrianVerm
public void execute() {

Optional<String> maybeString = Optional.of("foo");

String newString = maybeString

.map(this::runIfExist)

.orElse(runIfEmpty());

System.out.println(newString);

}

private String runIfExist(String str) {

System.out.println("only run if optional is filled ");

return str;

}

private String runIfEmpty() {

System.out.println("only run if empty");

return "empty";

}
only run if optional is filled
only run if empty
foo
orElseGet
@BrianVerm
public void execute() {

Optional<String> maybeString = Optional.of("foo");

String newString = maybeString

.map(this::runIfExist)

.orElseGet(() -> runIfEmpty());

System.out.println(newString);

}

private String runIfExist(String str) {

System.out.println("only run if optional is filled ");

return str;

}

private String runIfEmpty() {

System.out.println("only run if empty");

return "empty";

}
orElseGet
@BrianVerm
public void execute() {

Optional<String> maybeString = Optional.of("foo");

String newString = maybeString

.map(this::runIfExist)

.orElseGet(() -> runIfEmpty());

System.out.println(newString);

}

private String runIfExist(String str) {

System.out.println("only run if optional is filled ");

return str;

}

private String runIfEmpty() {

System.out.println("only run if empty");

return "empty";

}
only run if optional is filled
foo
what else?
- When using orElse(x), make sure x doesn’t contain any side effects
- Only use orElse() to assign a default value
- Use orElseGet() to run an alternative flow
@BrianVerm
List of Optionals
@BrianVerm
beerLib.stream()
.map(Beer::getDescription) //returns optional
List of Optionals
@BrianVerm
//java 8 style
beerLib.stream()
.map(Beer::getDescription) //returns optional
.filter(Optional::isPresent)
.map(Optional::get)
.forEach(System.out::println);
List of Optionals
@BrianVerm
//java 8 flatMap
beerLib.stream()
.map(Beer::getDescription) //returns optional
.flatMap(o -> o.map(Stream::of).orElse(Stream.empty()))
.forEach(System.out::println);
List of Optionals
@BrianVerm
//java 9 flatMap
beerLib.stream()
.map(Beer::getDescription) //returns optional
.flatMap(Optional::stream)
.forEach(System.out::println);
Exceptions
@BrianVerm
Checked Exceptions & Lambda
@BrianVerm


public Beer doSomething(Beer beer) throws IsEmptyException { …}
Function <Beer,Beer> fBeer = beer -> doSomething(beer)
Checked Exceptions & Lambda
@BrianVerm
public Beer doSomething(Beer beer) throws IsEmptyException { …}
Function <Beer,Beer> fBeer = beer -> doSomething(beer)
Checked Exceptions & Lambda
@BrianVerm
public Beer doSomething(Beer beer) throws IsEmptyException { …}
beerLib.stream()
.map(beer -> {

try{

return doSomething(beer);

} catch (IsEmptyException e) {

throw new RuntimeException(e);

}

};)
.collect(Collectors.toList());
//not very pretty
Checked Exceptions & Lambda
@BrianVerm
public Beer doSomething(Beer beer) throws IsEmptyException { …}
private Beer wrappedDoSomeThing(Beer beer) {

try{

return doSomething(beer);

} catch (IsEmptyException e) {

throw new RuntimeException(e);

}

}
beerLib.stream()
.map(this::wrappedDoSomeThing)
.collect(Collectors.toList());
Exception Utility
@BrianVerm
@FunctionalInterface

public interface CheckedFunction<T, R> {

public R apply(T t) throws Exception;

}
public static <T, R> Function<T, R> wrap(CheckedFunction<T, R> function) {

return t -> {

try {

return function.apply(t);

} catch (Exception ex) {

throw new RuntimeException(ex);

}

};

};
beerLib.stream()
.map(wrap(beer -> doSomething(beer)))
.collect(Collectors.toList());
Either type
@BrianVerm
public class Either<L, R> {
private final L left;
private final R right;
private Either(L left, R right) {
this.left = left;
this.right = right;
}
public static <L,R> Either<L,R> Left( L value) {
return new Either(value, null);
}
public static <L,R> Either<L,R> Right( R value) {
return new Either(null, value);
} …
}
Either type
@BrianVerm
private Either<Exception, String> canGoWrong(Integer input) {
if (input > 10) {
return Either.Left(new RuntimeException("larger then 10"));
}
return Either.Right("["+input+"]");
}
List<Either<Exception, String>> canGoWrongs = IntStream.range(0,12)
.mapToObj(i -> canGoWrong(i))
.collect(Collectors.toList());
Either type
@BrianVerm
private Either<Exception, String> canGoWrong(Integer input) {
if (input > 10) {
return Either.Left(new RuntimeException("larger then 10"));
}
return Either.Right("["+input+"]");
}
List<Either<Exception, String>> canGoWrongs = IntStream.range(0,12)
.mapToObj(i -> canGoWrong(i))
.collect(Collectors.toList());
canGoWrongs.stream()
.map(e -> e.mapRight(s -> s.toUpperCase()))
.flatMap(o -> o.map(Stream::of).orElseGet(Stream::empty))
.forEach(System.out::println);
Try
- Failure (Exception)
- Success (Type)
- VAVR
@BrianVerm
Try
- Failure (Exception)
- Success (Type)
- VAVR
@BrianVerm
List<Try<String>> output = teams.stream()
.map(CheckedFunction1.liftTry(this::mayThrowException))
.collect(Collectors.toList());
@BrianVerm
@BrianVerm
Brian Vermeer
@BrianVerm
brianvermeer@snyk.io

Writing better functional java code - devnexus

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
    Don’t do tomuch in a single lambda @BrianVerm
  • 7.
    Lambda Expression In computer programming,a lambda expression is a function definition that is not bound to an identifier. Anonymous functions are often:[1] • arguments being passed to higher-order functions, or • used for constructing the result of a higher-order function that needs to return a function. @BrianVerm
  • 8.
    Lambda in Java AnonymousInner functions Satisfy a Functional Interface They only exist in runtime Single Line <input> -> <output> or Block <input> -> { function body } @BrianVerm
  • 9.
    Example @BrianVerm public void execute(){ Beer grolsch = new Beer("Grolsch", 4.3); String result = handleBeer(grolsch, beer -> grolsch.getName() + "-" + grolsch.getAlcohol()); System.out.println(result); } private String handleBeer(Beer beer, Function<Beer, String> func) { System.out.println("handling beer " + beer.getName()); return func.apply(beer); } //Output : Grolsch-4.3
  • 10.
    Example @BrianVerm public void execute(){ Beer grolsch = new Beer("Grolsch", 4.3); String result = handleBeer(grolsch, beer -> grolsch.getName() + "-" + grolsch.getAlcohol()); System.out.println(result); } private String handleBeer(Beer beer, Function<Beer, String> func) { System.out.println("handling beer " + beer.getName()); return func.apply(beer); } //Output : Grolsch-4.3
  • 11.
    Don’t do BlockLambda @BrianVerm beer -> { String name; if (beer.getName().contains(" ")) { name = beer.getName().replace(" ", ""); } else { name = beer.getName(); } try { name += Integer.parseInt(beer.getAlcoholPrecentage().toString()); } catch (NumberFormatException nfe) { name += beer.getAlcoholPrecentage(); } return name; }
  • 12.
    Transform to amethode @BrianVerm private String createFullName (Beer beer){ String name; if (beer.getName().contains(" ")) { name = beer.getName().replace(" ", ""); } else { name = beer.getName(); } try { name += Integer.parseInt(beer.getAlcoholPrecentage().toString()); } catch (NumberFormatException nfe) { name += beer.getAlcoholPrecentage(); } return name; } String result = handleBeer(grolsch, this::createFullName);
  • 13.
  • 14.
    A stream isNOT a data structure @BrianVerm
  • 15.
    A stream isNOT a data structure @BrianVerm A stream is NOT a data structure
  • 16.
    What is Stream( in Java) Flow of data derived from a Collection Can create a pipeline of function that can be evaluated Intermediate result Lazy evaluated by nature Can transform data, cannot mutate data @BrianVerm
  • 17.
    JAVA Streams @BrianVerm stringLists.stream()
 .map(str ->str.toUpperCase())
 .collect(Collectors.toList()); Intermediate filter 
 distinct
 map 
 flatMap
 Terminal reduce
 collect
 toArray
 count
 max
 min
 limit 
 skip
 sorted
 peek findAny
 findFirst
 forEach
 allMatch
 anyMatch
  • 18.
    List<Beer> beers =getBeers();
 Stream<Beer> beerStream = beers.stream();
 
 beerStream.forEach(b ->System.out.println(b.getName())); //1 
 beerStream.forEach(b ->System.out.println(b.getAlcohol())); //2 Only use a Stream once Line 2 will give: 
 
 java.lang.IllegalStateException: stream has already been operated upon or closed @BrianVerm
  • 19.
    public Stream<Beer> getMeMyBeers() publicvoid execute() { getMeMyBeers() //don’t know if it is consumed yet!!!! … } Be careful with returning a Stream @BrianVerm
  • 20.
    Returning a Stream Privateintermediate function When new stream is created every time When result is very large or might be infinite By default return a collection @BrianVerm
  • 21.
  • 22.
    Functional Programming In computerscience, functional programming is a programming paradigma style of building the structure and elements of computer programs that treats computation as the evaluation of mathematical functions and avoids changing-state and mutable data. It is a declarative programming paradigm, which means programming is done with expressions or declarations instead of statements. — wikipedia @BrianVerm
  • 23.
  • 24.
    @BrianVerm – Kevlin Henney “Askinga question should not change the answer, nor should asking it twice”
  • 25.
    Immutable objects Less movingparts Easier to reason about code No need to keep a mental map of the state an object is in. @BrianVerm
  • 26.
    Stream cannot mutate @BrianVerm privatefinal List<Beer> beers = List.of(new Beer("Heineken", 5.2), new Beer("Amstel", 5.1)); public void execute() { List<Beer> beersNew = beers.stream() .map(beer -> beer.setName(“foo”)) //not allowed .collect(Collectors.toList()); }
  • 27.
    Stream should notmutate ! @BrianVerm private class Beer { String name; Double alcohol; public Beer setName(String name){ this.name = name; return this; } } private final List<Beer> beers = List.of(new Beer("Heineken", 5.2), new Beer("Amstel", 5.1)); public void execute() { List<Beer> beersNew = beers.stream() .map(beer -> beer.setName("foo")) .collect(Collectors.toList()); System.out.println(beers); System.out.println(beersNew); }
  • 28.
    Stream should notmutate ! @BrianVerm private class Beer { String name; Double alcohol; public Beer setName(String name){ this.name = name; return this; } } private final List<Beer> beers = List.of(new Beer("Heineken", 5.2), new Beer("Amstel", 5.1)); public void execute() { List<Beer> beersNew = beers.stream() .map(beer -> beer.setName("foo")) .collect(Collectors.toList()); System.out.println(beers); System.out.println(beersNew); }
  • 29.
    Return a newcopy @BrianVerm private class Beer { String name; Double alcohol; public Beer withName(String name){ return new Beer(name, this.alcohol); } } private final List<Beer> beers = List.of(new Beer("Heineken", 5.2), new Beer("Amstel", 5.1)); public void execute() { List<Beer> beersNew = beers.stream() .map(beer -> beer.withName("foo")) .collect(Collectors.toList()); System.out.println(beers); System.out.println(beersNew); }
  • 30.
  • 31.
    forEach() Terminal functional ona stream. Takes a consumer. Can be used to apply side effects. for-loop without the external iterator @BrianVerm
  • 32.
    simple forEach example @BrianVerm List<String>names = List.of("James", "Trisha", "Joshua", "Jessica","Simon", “Heather”, “Roberto”); names.stream() .map(String::toUpperCase) .forEach(System.out::println);
  • 33.
    mutation with forEach @BrianVerm List<Beer>beers = List.of(new Beer("Heineken", 5.2), new Beer("Amstel", 5.1)); beers.stream() .forEach(beer -> beer.setAlcohol(0.0));
  • 34.
    overusing forEach @BrianVerm private classBeer { String name; Double alcohol; List<String> reviews; Integer rating; } List<Beer> beers = List.of(new Beer("Heineken", 5.2), new Beer("Amstel", 5.1)); //enrich with ratings beers.stream().forEach(beer -> beer.setRating(findRating(beer.getName()))); //enrich with reviews beers.stream().forEach(beer -> beer.setReviews(findReviews(beer.getName()))); …
  • 35.
  • 36.
    Assignment Beers -> Brewer-> Country I want the first 3 unique brewers countries from the beer library as comma separated String @BrianVerm beerLib.stream() .map(Beer::getBrewer) .distinct() .limit(3) .map(Brewer::getCountry) .map(String::toUpperCase) .collect(Collectors.joining(“,”));
  • 37.
    Assignment Beers -> Brewer-> Country I want the first 3 unique brewers countries from the beer library as comma separated String @BrianVerm beerLib.stream() .map(Beer::getBrewer) .distinct() .limit(3) .map(Brewer::getCountry) .map(String::toUpperCase) .collect(Collectors.joining(“,”)); // wrong
  • 38.
    Assignment Beers -> Brewer-> Country I want the first 3 unique brewers countries from the beer library as comma separated String @BrianVerm beerLib.stream() .map(Beer::getBrewer) .map(Brewer::getCountry) .map(String::toUpperCase) .distinct() .limit(3) .collect(Collectors.joining(“,”)); // correct
  • 39.
    Infinite stream @BrianVerm IntStream.iterate(0, i-> ( i + 1) % 2)
 .distinct()
 .limit(10)
 .forEach(i -> System.out.println(i));
  • 40.
    Infinite stream @BrianVerm IntStream.iterate(0, i-> ( i + 1) % 2)
 .distinct()
 .limit(10)
 .forEach(i -> System.out.println(i));
 // will run forever IntStream.iterate(0, i -> ( i + 1) % 2)
 .limit(10)
 .distinct()
 .forEach(i -> System.out.println(i)); //will terminate
  • 41.
    Infinite stream @BrianVerm IntStream.iterate(0, i-> ( i + 1) % 2)
 .parallel() .distinct()
 .limit(10)
 .forEach(i -> System.out.println(i));
 // will run forever on all threads.
  • 42.
    Solution 1. Look closelyat the order of operations
 - prevent incorrect answers
 
 2. Only use infinite streams when absolutely necessary. rather use: 
 @BrianVerm IntStream.range(0,10); IntStream.rangeClosed(0,10); IntStream.iterate(0, i -> i < 10, i -> i + 1); //java 9 and up
  • 43.
  • 44.
    Higher-Order function Taking afunction as a parameter of a method Insert behaviour instead of just data A new level of abstraction Reduce boilerplate / duplicate code @BrianVerm
  • 45.
    Duplicate code @BrianVerm private voidlogUpper(String str) {
 //super interesting code
 System.out.println(str.toUpperCase());
 //even more interesting code
 }
 
 private void logLower(String str) {
 //super interesting code
 System.out.println(str.toLowerCase());
 //even more interesting code
 }
  • 46.
    Insert behaviour @BrianVerm private voidlogUpper(String string) {
 doSuperCoolStuff(string, s -> s.toUpperCase());
 }
 
 private void logLower(String string) {
 doSuperCoolStuff(string, s -> s.toLowerCase());
 }
 
 private void doFoo(String str, Function<String, String> func) {
 //super interesting code
 System.out.println(func.apply(str));
 //even more interesting code
 }
  • 47.
    Real situation (WebshopBasket) MongoDB primary (read/write), multiple secondary (read) Writing only on the primary Mutating a basket: Read from Primary
 Edit
 Run rule engine
 Save to Primary
 @BrianVerm
  • 48.
    Real situation (WebshopBasket) MongoDB primary (read/write), multiple secondary (read) Writing only on the primary Mutating a basket: Read from Primary
 Edit
 Run rule engine
 Save to Primary
 @BrianVerm
  • 49.
    Mutator @BrianVerm public void mutatorById(Stringid, Consumer<Basket> action) { Basket basket = repoPrimary.findById(id); action.accept(basket); RuleEngine.run(basket) repoPrimary.save(basket); }
  • 50.
    Mutator @BrianVerm public void mutatorById(Stringid, Consumer<Basket> action) { Basket basket = repoPrimary.findById(id); action.accept(basket); RuleEngine.run(basket) repoPrimary.save(basket); } public void addItem(String uuid, Item item) { mutatorById(uuid, basket -> basket.addItem(item)); } public void removeItem(String uuid, Item item) { mutatorById(uuid, basket -> basket.removeItem(item)); }
  • 51.
  • 52.
    Optional Java’s implementation ofthe Maybe Monad Encapsulation to handle possible null value Consider it a wrapper where a value can be absent Force the user to unpack the Optional before using it. @BrianVerm
  • 53.
  • 54.
    public void execute(){ Optional<String> maybeString = getText(); String unpacked = maybeString.get(); } Unpack Optional @BrianVerm
  • 55.
    public void execute(){ Optional<String> maybeString = getText(); String unpacked = maybeString.get(); } Unpack Optional @BrianVerm NoSuchElementException
  • 56.
    public void execute(){ Optional<String> maybeString = getText(); String unpacked = maybeString.get(); } Unpack Optional @BrianVerm public void execute() { Optional<String> maybeString = getText(); if (maybeString.isPresent()) { String unpacked = maybeString.get(); } } NoSuchElementException
  • 57.
    Unpack Optional @BrianVerm public voidexecute() { Optional<String> maybeString = getText(); maybeString.ifPresent( str -> /* doSomething */ ); } public void execute() { Optional<String> maybeString = getText(); maybeString.map(str -> str + “."); }
  • 58.
  • 59.
    Alternative flow - orElse() -orElseGet() - orElseThrow() @BrianVerm
  • 60.
    orElseThrow @BrianVerm public void execute(){
 Optional<String> maybeString = Optional.empty();
 maybeString
 .map(this::runIfExist)
 .orElseThrow(() -> new RuntimeException("Optional was empty"));
 }
  • 61.
    orElse @BrianVerm public void execute(){
 Optional<String> maybeString = Optional.of("foo");
 String newString = maybeString
 .map(this::runIfExist)
 .orElse(runIfEmpty());
 System.out.println(newString);
 }
 private String runIfExist(String str) {
 System.out.println("only run if optional is filled ");
 return str;
 }
 private String runIfEmpty() {
 System.out.println("only run if empty");
 return "empty";
 }
  • 62.
    orElse @BrianVerm public void execute(){
 Optional<String> maybeString = Optional.of("foo");
 String newString = maybeString
 .map(this::runIfExist)
 .orElse(runIfEmpty());
 System.out.println(newString);
 }
 private String runIfExist(String str) {
 System.out.println("only run if optional is filled ");
 return str;
 }
 private String runIfEmpty() {
 System.out.println("only run if empty");
 return "empty";
 } only run if optional is filled only run if empty foo
  • 63.
    orElseGet @BrianVerm public void execute(){
 Optional<String> maybeString = Optional.of("foo");
 String newString = maybeString
 .map(this::runIfExist)
 .orElseGet(() -> runIfEmpty());
 System.out.println(newString);
 }
 private String runIfExist(String str) {
 System.out.println("only run if optional is filled ");
 return str;
 }
 private String runIfEmpty() {
 System.out.println("only run if empty");
 return "empty";
 }
  • 64.
    orElseGet @BrianVerm public void execute(){
 Optional<String> maybeString = Optional.of("foo");
 String newString = maybeString
 .map(this::runIfExist)
 .orElseGet(() -> runIfEmpty());
 System.out.println(newString);
 }
 private String runIfExist(String str) {
 System.out.println("only run if optional is filled ");
 return str;
 }
 private String runIfEmpty() {
 System.out.println("only run if empty");
 return "empty";
 } only run if optional is filled foo
  • 65.
    what else? - Whenusing orElse(x), make sure x doesn’t contain any side effects - Only use orElse() to assign a default value - Use orElseGet() to run an alternative flow @BrianVerm
  • 66.
  • 67.
    List of Optionals @BrianVerm //java8 style beerLib.stream() .map(Beer::getDescription) //returns optional .filter(Optional::isPresent) .map(Optional::get) .forEach(System.out::println);
  • 68.
    List of Optionals @BrianVerm //java8 flatMap beerLib.stream() .map(Beer::getDescription) //returns optional .flatMap(o -> o.map(Stream::of).orElse(Stream.empty())) .forEach(System.out::println);
  • 69.
    List of Optionals @BrianVerm //java9 flatMap beerLib.stream() .map(Beer::getDescription) //returns optional .flatMap(Optional::stream) .forEach(System.out::println);
  • 70.
  • 71.
    Checked Exceptions &Lambda @BrianVerm 
 public Beer doSomething(Beer beer) throws IsEmptyException { …} Function <Beer,Beer> fBeer = beer -> doSomething(beer)
  • 72.
    Checked Exceptions &Lambda @BrianVerm public Beer doSomething(Beer beer) throws IsEmptyException { …} Function <Beer,Beer> fBeer = beer -> doSomething(beer)
  • 73.
    Checked Exceptions &Lambda @BrianVerm public Beer doSomething(Beer beer) throws IsEmptyException { …} beerLib.stream() .map(beer -> {
 try{
 return doSomething(beer);
 } catch (IsEmptyException e) {
 throw new RuntimeException(e);
 }
 };) .collect(Collectors.toList()); //not very pretty
  • 74.
    Checked Exceptions &Lambda @BrianVerm public Beer doSomething(Beer beer) throws IsEmptyException { …} private Beer wrappedDoSomeThing(Beer beer) {
 try{
 return doSomething(beer);
 } catch (IsEmptyException e) {
 throw new RuntimeException(e);
 }
 } beerLib.stream() .map(this::wrappedDoSomeThing) .collect(Collectors.toList());
  • 75.
    Exception Utility @BrianVerm @FunctionalInterface
 public interfaceCheckedFunction<T, R> {
 public R apply(T t) throws Exception;
 } public static <T, R> Function<T, R> wrap(CheckedFunction<T, R> function) {
 return t -> {
 try {
 return function.apply(t);
 } catch (Exception ex) {
 throw new RuntimeException(ex);
 }
 };
 }; beerLib.stream() .map(wrap(beer -> doSomething(beer))) .collect(Collectors.toList());
  • 76.
    Either type @BrianVerm public classEither<L, R> { private final L left; private final R right; private Either(L left, R right) { this.left = left; this.right = right; } public static <L,R> Either<L,R> Left( L value) { return new Either(value, null); } public static <L,R> Either<L,R> Right( R value) { return new Either(null, value); } … }
  • 77.
    Either type @BrianVerm private Either<Exception,String> canGoWrong(Integer input) { if (input > 10) { return Either.Left(new RuntimeException("larger then 10")); } return Either.Right("["+input+"]"); } List<Either<Exception, String>> canGoWrongs = IntStream.range(0,12) .mapToObj(i -> canGoWrong(i)) .collect(Collectors.toList());
  • 78.
    Either type @BrianVerm private Either<Exception,String> canGoWrong(Integer input) { if (input > 10) { return Either.Left(new RuntimeException("larger then 10")); } return Either.Right("["+input+"]"); } List<Either<Exception, String>> canGoWrongs = IntStream.range(0,12) .mapToObj(i -> canGoWrong(i)) .collect(Collectors.toList()); canGoWrongs.stream() .map(e -> e.mapRight(s -> s.toUpperCase())) .flatMap(o -> o.map(Stream::of).orElseGet(Stream::empty)) .forEach(System.out::println);
  • 79.
    Try - Failure (Exception) -Success (Type) - VAVR @BrianVerm
  • 80.
    Try - Failure (Exception) -Success (Type) - VAVR @BrianVerm List<Try<String>> output = teams.stream() .map(CheckedFunction1.liftTry(this::mayThrowException)) .collect(Collectors.toList());
  • 81.
  • 82.