Upload
wakaleo-consulting
View
7.438
Download
0
Tags:
Embed Size (px)
DESCRIPTION
Functional programming is all the rage. It can undoubtedly produce cleaner and more expressive code, but why switch to Scala or wait for Java 8? In fact, there are many ways to dramatically improve your Java code quality by using a more functional style today. This presentation discusses how techniques such as functional programming, using fluent APIs, and other clean coding practices can make your code more concise, more readable, and much easier to maintain. But more importantly, you will learn about libraries such as LambdaJ and Google Guava that make this possible today with your existing Java code base.
Citation preview
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);