Date post: | 10-May-2015 |
Category: |
Documents |
Upload: | wakaleo-consulting |
View: | 7,437 times |
Download: | 0 times |
John Ferguson [email protected] h2p://www.wakaleo.com
Twi2er: wakaleo
Improve Your Java Code, Functional-Style - Now!
So who is this guy, anyway?
John Ferguson Smart
ConsultantTrainerMentorAuthorSpeakerCoder
f(x,y) = x + y
Func:onal Programming is the new black
Immutability is your friend
Avoid nasty side-‐effects
Benefit from concurrency
No more messy loops
HowWhat
Func:onal Programming in Java
Coming to a JVM near you the summer of 2013
List<String> names = Arrays.asList("Aristotle", "Plato", "Socrates", "Pythagoras");
List<String> filteredNames = names .filter(name -> name.startsWith("P")) .into(new ArrayList<String>());
assertThat(filteredNames, contains("Plato", "Pythagoras"));
Java 8 supports Lambda Expressions
names.filter(name -> name.startsWith("P")) .into(new ArrayList<String>());
["Aristotle", "Plato", "Socrates", "Pythagoras"]
["Plato", "Pythagoras"]
Java 8 supports Lambda Expressions
public class Pet { private String name; private Species species;
Pet(String name, Species species) {...}
public String getName() {...} public Species getSpecies() {...}
@Override public boolean equals(Object o) {...}
@Override public int hashCode() {...}
enum Species { Cat, Dog, GuineaPig; Pet called(String name) { return new Pet(name, this); } }}
Java 8 supports Lambda Expressions
public class Pet { private String name; private Species species;
Pet(String name, Species species) {...}
public String getName() {...} public Species getSpecies() {...}
@Override public boolean equals(Object o) {...}
@Override public int hashCode() {...}
enum Species { Cat, Dog, GuineaPig; Pet called(String name) { return new Pet(name, this); } }}
List<Pet> pets = Arrays.asList(Cat.called("Ginger"), Dog.called("Spot"), GuineaPig.called("Fluffy"), Dog.called("Rover"));
List<Pet> sortedDogs = pets.filter(a -> a.getSpecies() == Dog) .sorted((a, b) -> a.getName().compareTo(b.getName())) .into(new ArrayList<Pet>());
assertThat(sortedDogs, contains(Dog.called("Rover"), Dog.called("Spot")));
Java 8 supports Lambda Expressions
pets.filter(a -> a.getSpecies() == Dog) .sorted((a, b) -> a.getName().compareTo(b.getName())) .into(new ArrayList<Pet>());
[Ginger, Spot, Fluffy, Rover]
[Rover, Spot]
Java 8 supports Lambda Expressions
pets.filter(a -> a.getSpecies() == Dog) .map(e -> { return e.getName(); }) .sorted((a, b) -> a.compareTo(b)) .into(new ArrayList<Pet>());
[Ginger, Spot, Fluffy, Rover]
["Rover", "Spot"]
Java 8 supports Lambda Expressions
Predicate<Pet> carnivores = (pet) -> (pet.getSpecies() == Dog || pet.getSpecies() == Cat);
List<Pet> carnivorousPets = pets.filter(carnivores).into(new ArrayList<Pet>());
[Ginger, Spot, Fluffy, Rover]
["Ginger", "Spot", "Rover"]
Java 8 supports Lambda Expressions
pets.filter((pet) -> (pet.getSpecies() == Dog || pet.getSpecies() == Cat)) .into(new ArrayList<Pet>());
[Ginger, Spot, Fluffy, Rover]
["Ginger", "Spot", "Rover"]
Java 8 supports Lambda Expressions
Java 8 supports Lambda Expressions
public class VetStay { private Pet pet; private Date startOfStay; private String diagnosis;
public VetStay(Pet pet, Date startOfStay, String diagnosis) { this.pet = pet; this.startOfStay = startOfStay; this.diagnosis = diagnosis; }}
List<VetStay> vetStays = pets.map(pet -> { return new VetStay(pet, new Date(), "sore paw");})
.into(new ArrayList<VetStay>());
List<Pet> pets = Arrays.asList(Cat.called("Ginger"), Dog.called("Spot"), GuineaPig.called("Fluffy"), Dog.called("Rover"));
assert pets.anyMatch(pet -> pet.getSpecies() == Dog)
Java 8 supports Lambda Expressions
But Func:onal Programming in Java today?
Surely this is madness!
Google Guava
Defensive
Thread-safe
Efficient
Immutable Collec:ons
List<String> colors = ImmutableList.of("red", "green", "blue");
Creating an immutable list
Set<String> colors = ImmutableSet. of("red", "green", "blue");Set<String> myColors = ImmutableSet.copyOf(colors);
Creating an immutable copy
Immutable Collec:ons
No more returning Null
“Null sucks.” - Doug Lea
“I call it my billion-dollar mistake.” - Sir Tony Hoare
No more returning Null
interface ClientService { Client findByName(String name); }
What does a null return value mean?
No matching client found (but we were expecting one)?
No matching client found (but we’re cool with that)?
Something went wrong?
No more returning Null
Optional<Client> clientOptional = clientService.findByName("Smith");if (clientOptional.isPresent()) { Client client = clientOptional.get();} else { // No client was found}
interface ClientService { Optional<Client> findByName(String name); }
Sometimes we might not return a client
This forces us to cater for this case in our code
No more returning Null
Color colorToUse = person.getFavoriteColor().or(Blue)
class Person { Optional<Color> getFavoriteColor(); }
A person may not have a favorite color
If not, use Blue
And lots of other stuff...
No more returning Null
interface ClientService { Client findByName(String name); }
What does a null return value mean?
No matching client found (but we were expecting one)?
No matching client found (but we’re cool with that)?
Something went wrong?
No more returning Null
Optional<Client> clientOptional = clientService.findByName("Smith");if (clientOptional.isPresent()) { Client client = clientOptional.get();} else { // No client was found}
interface ClientService { Optional<Client> findByName(String name); }
Sometimes we might not return a client
This forces us to cater for this case in our code
No more returning Null
Color colorToUse = person.getFavoriteColor().or(Blue)
class Person { Optional<Color> getFavoriteColor(); }
A person may not have a favorite color
If not, use Blue
– DSL for manipulating collections in a functional style– Replace loops with more concise and readable code
lambdaj
filter
LambdaJ support many high level collection-related functions
sort
extract
aggregate
convert
group
import static ch.lambdaj.Lambda.filter;import static org.hamcrest.Matchers.startsWith;...List<String> names = Arrays.asList("Aristotle", "Plato", "Socrates", "Pythagoras");
List<String> filteredNames = filter(startsWith("P"), names);
assertThat(filteredNames, contains("Plato", "Pythagoras"));
Filtering in LambdaJ
LambdaJ
Hamcrest
filter(startsWith("P"), names)
["Aristotle", "Plato", "Socrates", "Pythagoras"]
["Plato", "Pythagoras"]
Filtering in LambdaJ
List<String> filteredNames = new ArrayList<>();for(String name : names) { if (name.startsWith("P")) { filteredNames.add(name); }}
Old-style Java
names.filter(name -> name.startsWith("P")) .into(new ArrayList<String>());
Java 8
filter(startsWith("P"), names)
Lambda J
Filtering in LambdaJ
List<Pet> pets = Arrays.asList(Cat.called("Ginger"), Dog.called("Spot"), GuineaPig.called("Fluffy"), Dog.called("Rover"));
List<Pet> sortedDogs = filter(having(on(Pet.class).getSpecies(), is(Dog)), pets);
assertThat(sortedDogs, contains(Dog.called("Rover"), Dog.called("Spot")));
Filtering in LambdaJ
Filtering in LambdaJ
filter(having(on(Pet.class).getSpecies(), is(Dog)), pets);
[Ginger, Spot, Fluffy, Rover]
[Rover, Spot]
Sor:ng in LambdaJ
sort(sortedDogs, on(Pet.class).getName());
[Ginger, Spot, Fluffy, Rover]
[Fluffy, Ginger, Rover, Spot]
Filtering and Sor:ng in LambdaJ
List<Pet> filteredDogs = filter(having(on(Pet.class).getSpecies(), is(Dog)), pets);List<Pet> sortedDogs = sort(filteredDogs, on(Pet.class).getName());
[Ginger, Spot, Fluffy, Rover]
[Rover, Spot]
Extrac:ng in LambdaJ
pets.filter(a -> a.getSpecies() == Dog) .map(e -> { return e.getName(); }) .sorted((a, b) -> a.compareTo(b)) .into(new ArrayList<Pet>());
[Ginger, Spot, Fluffy, Rover]
["Rover", "Spot"]
Extracting with Java 8
Extrac:ng in LambdaJ
List<Pet> dogs = filter(having(on(Pet.class).getSpecies(), is(Dog)), pets);List<String> dogNames = extract(dogs, on(Pet.class).getName());List<String> sortedDogNames = sort(dogNames,on(String.class));
[Ginger, Spot, Fluffy, Rover]
["Rover", "Spot"]
Extracting with LambdaJ
Extrac:ng in LambdaJ
[Ginger, Spot, Fluffy, Rover]
["Rover", "Spot"]
List<Pet> filteredDogs = new ArrayList();for(Pet pet : pets) { if (pet.getSpecies() == Dog) { filteredDogs.add(pet); }}Collections.sort(filteredDogs, new Comparator<Pet>() { @Override public int compare(Pet pet, Pet pet1) { return pet.getName().compareTo(pet1.getName()); }});
Extracting with old-style Java
Extrac:ng in LambdaJ
List<Pet> dogs = filter(having(on(Pet.class).getSpecies(), is(Dog)), pets);List<String> dogNames = extract(dogs, on(Pet.class).getName());List<String> sortedDogNames = sort(dogNames,on(String.class));
[Ginger, Spot, Fluffy, Rover]
["Rover", "Spot"]
Extracting with LambdaJ
Conver:ng in LambdaJ
List<VetStay> vetStays = pets.map(pet -> { return new VetStay(pet, new Date(), "sore paw");}) .into(new ArrayList<VetStay>());
Converting with Java 8
convert(pets, toVetStay());
private Converter<Pet, VetStay> toVetStay() { return new Converter<Pet, VetStay>() { @Override public VetStay convert(Pet pet) { return new VetStay(pet, new Date(), "sore paw"); } };}
Converting with LambdaJ
Grouping in LambdaJ
Group<Pet> petsBySpecies = group(pets, by(on(Pet.class).getSpecies()));
List<Pet> dogs = petsBySpecies.find(Dog);
[Ginger, Spot, Fluffy, Rover]
[Spot,Rover]
FlaOening in LambdaJ
List<List<Pet>> petsBySpecies = ...
List<Pet> allPets = flatten(petsBySpecies);
[[Spot, Rover], [Ginger], [Fluffy]]
[Spot, Rover, Ginger, Fluffy]
Checking existence in LambdaJ
exists(pets, having(on(Pet.class).getSpecies(), is(Cat)))
[Spot, Rover, Ginger, Fluffy]
true
Using Predicates in LambdaJ
Predicate<Pet> carnivores = (pet) -> (pet.getSpecies() == Dog || pet.getSpecies() == Cat);
List<Pet> carnivorousPets = pets.filter(carnivores).into(new ArrayList<Pet>());
Java 8
Matcher<Pet> carnivore = new Predicate<Pet>() { public boolean apply(Pet pet) { return (pet.getSpecies() == Dog || pet.getSpecies() == Cat); }};
List<Pet> carnivores = filter(carnivore, pets);LambdaJ
Using Aggregates in LambdaJ
List<Pet> pets = Arrays.asList(Cat.called("Ginger"), Dog.called("Spot"), GuineaPig.called("Fluffy"), Dog.called("Rover"));
int ageOfOldestPet = maxFrom(pets).getAge();int ageOfYoungestPet = minFrom(pets).getAge();int totalPetAges = sumFrom(pets).getAge();
Some real-‐world examples
SUCCESS
TestOutcome 1
SUCCESS
TestOutcome 2
FAILURE
TestOutcome 3
PENDING
TestOutcome 4
SUCCESS
TestOutcome 1
SUCCESS
TestOutcome 2
Some real-‐world examples
SUCCESS
TestOutcome 1
SUCCESS
TestOutcome 2
FAILURE
TestOutcome 3
PENDING
TestOutcome 4
SUCCESS
TestOutcome 1
SUCCESS
TestOutcome 2
filter(having(on(TestOutcome.class).getResult(), is(SUCCESS)), outcomes);
Some real-‐world examples
SUCCESS
TestOutcome 1
SUCCESS
TestOutcome 2
FAILURE
TestOutcome 3
PENDING
TestOutcome 4
SUCCESS
TestOutcome 1
SUCCESS
TestOutcome 2
filter(withResult(SUCCESS)), outcomes);
private Matcher<?> withResult(TestResult expectedResult) { return having(on(TestOutcome.class).getResult(), is(expectedResult));}
Some real-‐world examples
SUCCESS
TestOutcome 1
SUCCESS
TestOutcome 2
FAILURE
TestOutcome 3
PENDING
TestOutcome 4
SUCCESS
TestOutcome 1
SUCCESS
TestOutcome 2
filter(anyOf(withResult(SUCCESS), withResult(PENDING)), outcomes);
PENDING
TestOutcome 4
Some real-‐world examples
SUCCESS
TestOutcome 1
SUCCESS
TestOutcome 2
FAILURE
TestOutcome 3
PENDING
TestOutcome 4
TestOutcome 2
Step 1
Step 2
Step 3
Step 4
Some real-‐world examples
TagProvider1
TagProvider2
TagTag 1Tag
TagTagTagTag
TagTag 1TagTagTagTagTag
flatten(extract(TagProviders, on(TagProvider.class).getTags()));
And performance in all that?
Keeping things maintainable
“Excessive use of Guava's functional programming idioms can lead to verbose, confusing, unreadable, and inefficient code.” - the Guava team
Can also be true of LambdaJ
Keeping things maintainable
RULE 1
Use a functional style when it makes the intent more readable
Keeping things maintainable
sort(extract(filter(having(on(Pet.class).getSpecies(), is(Dog)), pets), on(Pet.class).getName()), on(String.class));
List<Pet> dogs = filter(having(on(Pet.class).getSpecies(), is(Dog)), pets);List<String> dogNames = extract(dogs, on(Pet.class).getName());List<String> sortedDogNames = sort(dogNames, on(String.class));
Keeping things maintainable
RULE 2
One-liners are not always better
Keeping things maintainable
convert(pets, new Converter<Pet, VetStay>() { @Override public VetStay convert(Pet pet) { return new VetStay(pet, new Date(), "sore paw"); } });
convert(pets, toVetStay());
private Converter<Pet, VetStay> toVetStay() { return new Converter<Pet, VetStay>() { @Override public VetStay convert(Pet pet) { return new VetStay(pet, new Date(), "sore paw"); } };}
Keeping things maintainable
RULE 3
Write your own domain-specific matchers
Keeping things maintainable
filter(withResult(SUCCESS)), outcomes);
private Matcher<?> withResult(TestResult expectedResult) { return having(on(TestOutcome.class).getResult(), is(expectedResult));}
select(anyOf(withResult(SKIPPED), withResult(IGNORED)), outcomes);
select(not(withResult(SUCCESS))), outcomes);