Java 8 Predicate Interface using Lambdas

I am going to tell you a short programming story. Let us assume that we have a system which deals with a list of Person objects. Person class has name, age, citizenship and gender.


public class Person {
private String name;
private int age;
private Gender gender;
private Citizenship citizenship;
}

view raw

Person.java

hosted with ❤ by GitHub

One fine day your manager comes to your desk.

Manager: The current system should be able to give us a list of all the men.

Developer: That is easy.

Manager:  OK, this is a very critical requirement, check in the code in the next 1 hour.

You are brimming with happiness, you have done this so many times and then you say to yourself, that’s  easy!  I just need to initialize an empty collection, iterate over the list of collection, add an if condition and that’s it. This is what you come up with:


private static List<Person> getAllMen(List<Person> persons)
{
List<Person> men = new ArrayList<>();
for(Person person : persons)
{
if(person.getGender() == Gender.MALE)
{
men.add(person);
}
}
return men;
}

Before checking in the code, you look through the code again, notice the new List of men objects, the iteration and if condition. You feel a great sense of accomplishment and send an email to your manager and possibly your colleagues that your task is completed. You then hop away for lunch.

After a heavy lunch, you come back on your desk and notice the manager walk towards you, it looks like he has a very serious and difficult requirement.

Manager: Good coding, I have a new requirement for you. Now the system needs to fetch the list of all the women from the database.

You: Okay, consider it done.

Manager: Alright, I need you to do this in 45 minutes.

What do you do?  You know the class name, you open it, look at the getAllMen() and use the most favorite operations,  “copy” , “paste” and then rename the method name and change the condition. This is what you come up with:


private static List<Person> getAllWomen(List<Person> persons) {
List<Person> men = new ArrayList<>();
for(Person person : persons)
{
if(person.getGender() == Gender.FEMALE)
{
men.add(person);
}
}
return men;
}

There is a big smile on your face as you look at your watch, you accomplished this code in 5 full minutes. You look at your code and say to yourself, create new list, iterate, if condition. It looks good and then check in the code, but send the email after 40 minutes just to make sure that your manager notices the 5 minutes that you saved.

Then you suddenly look at the code again, create new empty list, iterate, if condition…… But then you realize that you have forgotten to rename the variable. There is a little bit of panic but you are really fast, you rename that variable, men to women, check in that code so quickly that even Vin Diesel from the Fast and the Furious fame would be ashamed. Now, it’s time for a cup of tea. 

The modified version


private static List<Person> getAllWomen(List<Person> persons)
{
List<Person> women = new ArrayList<>();
for(Person person : persons)
{
if(person.getGender() == Gender.FEMALE)
{
women.add(person);
}
}
return women;
}

It’s 5:30 pm already, you know it is time to go home. But alas, the manager again walking towards you. You do not want to look at him this time, you stare at your PC as if you are really busy and have no time for anyone.

Manager: Good job, great coding, you are really fast but I need you to retrieve the list of all men who are citizens of USA.

Hmm, so, you look at your watch, he looks back at you..

Manager: I would like you to check in the code before you leave for the day.

How do you feel ? Frustrated. You promised to take your girlfriend on that coffee date at 7 pm. The manager leaves your table. You roll up your sleeves and start copy, pasting and profusely start typing.  You look at your code and say to yourself, create new list, iterate, change if condition and this time…..change variable name too!

This time you are more careful to rename the variables, you go through the code once, this time everything looks flawless. Time to check in!


private static List<Person> getAllMenUSA(List<Person> persons)
{
List<Person> menUSA = new ArrayList<>();
for(Person person : persons)
{
if(person.getGender() == Gender.MALE && person.getCitizenship() == Citizenship.USA)
{
menUSA.add(person);
}
}
return menUSA;
}

You feel a great sense of accomplishment, 3 full methods in the entire day and pray that there are no bugs. It’s 6 pm, you send an email to your manager and everyone is now happy. Suddenly you look at all the 3 methods and how do you feel?  WASTED!  You ask yourself , can I change something here? You look at the 3 methods one more time and realize that the if condition that checks if the gender is male or female seems repetitive. You look at your watch once again, it is already 6:15! You decide to leave.

On your way to meet your girlfriend, as you are riding your bike, you feel that the air around is polluted but you know deep down inside about polluting the code base. You meet your girl friend, there is a big smile on her face but a lot of dirt on your face caused due to the code pollution.You have also committed another sin, you have forgotten to pick up flowers for her!

You wish you could have at least written a separate method to check if the gender is male. The meeting with your girlfriend does not go that good, well the code…..we all know about that.

The next day, you wake up early, have a shower, and think about the new method you are going to write. You feel really good and go early to office. You bring all your experience to the table and start writing a new method which can be reused.


private boolean isMan(Gender gender){
return gender == Gender.MALE;
}

view raw

IsMan.java

hosted with ❤ by GitHub

 As you are belting out that code, your chest swells up as you see that method name:  isMan(). You refactor your old code and change all occurrences of the condition  

             if(person.getGender() == Gender.MALE) to isMan()

You look at your code and say to yourself, new list, iterate, if condition, add to list. You give yourself a pat on your back but this time you do not send an email to your manager.  Well, you don’t want him to know that you made a change to your own code. Time for a morning tea break!

You are back on your desk, the manager has not come to your desk for a long time. You wonder if there are bugs in your code. But just then, there is a ping from the manager……

Manager: Good morning, got a new requirement. The system needs to fetch all women who are American citizens.

You (say this to yourself ☺) : Easy! Copy, Paste, rename, change if!


private static List<Person> getAllWomenUSA(List<Person> persons)
{
List<Person> womenUSA = new ArrayList<>();
for(Person person : persons)
{
if(person.getGender() == Gender.FEMALE && person.getCitizenship() == Citizenship.USA)
{
womenUSA.add(person);
}
}
return womenUSA;
}

This time, you look at your code a few more times and realize that you want to refactor anything that repeats. You keep looking at your code and realize that you could write a method isWoman and change all if conditions to use that. You quickly do that. How? Copy, Paste, rename!


private boolean isWoman(Gender gender)
{
return gender == Gender.FEMALE;
}

view raw

IsWoman.java

hosted with ❤ by GitHub

You stare at your 2 methods, getAllMenUSA and getAllWomenUSA(). You keep staring at it for a few minutes, you think about today’s meeting at 7 pm and of course, it is with your girlfriend. Now, you really want to refactor something. You realize that you can actually combine 2 methods into one.


private List<Person> getAllPersonByGenderAndCitizenhip(List<Person> persons, Gender gender, Citizenship citizenship)
{
List<Person> people = new ArrayList<>();
for(Person person : persons)
{
if(person.getGender() == gender && person.getCitizenship() == citizenship)
{
people.add(person);
}
}
return people;
}

You then remove the 2 methods written earlier. You replace the calls to the 2 methods by 1 method.

Earlier:  getAllMenUSA(persons)

                 getAllWomenUSA(persons);

Now:      getAllPersonByGenderAndCitizenship(persons,gender,citizenship)

Time for review, new list, iterate, if condition, check in. Time for a long lunch break!

You are back… Of course, it is now time for a new requirement. No free lunch!

Manager: Hey, need a list of men who are above the age of 35 but citizens of Canada.

You: Silent….

You look at your getAllPersonByGenderAndCitizenhip and say, okay, hmm, let’s add a new parameter to that function and create a new method by copy, pasting and renaming the old method.


private List<Person> getAllPersonByGenderCitizenshipAge(List<Person> persons, Gender gender, Citizenship citizenship, int age)
{
List<Person> people = new ArrayList<>();
for(Person person : persons){
if(person.getGender() == gender && person.getCitizenship() == citizenship && person.getAge() > age)
{
people.add(person);
}
}
return people;
}

getAllPersonByGenderAndCitizenshipAge(persons,Gender.MALE,Citizenship.CANADA, age);

New list, iterate, if condition(s), add to new list. Done! You let out a burp! Heavy lunch, a lot of coding, of course the food had to digest that quickly! Check in……This time, you get up, walk past your manager, and give him a smile. That look on your face for writing really good code!

It is 4 pm now. To kill the time, you read your code, all the methods, and realize that you are really writing the same code all over, but if one is right, they are all right and hence decide not to change anything.

Out of boredom, you then look at your colleague’s machine, he is reading about DRY – Don’t repeat yourself. You ask him what that is. He explains it you and you say oh, that’s easy. The look on your face, well, it’s as if you have been doing it since you were 5. You wonder if you can apply it your code!

Well, what the developer does not realize is the fact that even after refactoring some of the methods, these requirements can spiral out of control. There could be many more methods like:

private List<Person> getPersonByGender(List<Person> persons, Gender gender)

private List<Person> getPersonByGenderCitizenship(List<Person> persons,Gender gender, Citizenship citizenship)

private List<Person> getAllPersonByGenderCitizenshipAge (List<Person> persons,Gender gender, Citizenship citizenship, Age age)

All this is only leading to copy, paste, and repeat. If you look at all the methods above, what is really changing is the “if” condition. Everything else remains the same and yet we are duplicating code. How do we solve this? How do we isolate the “if” condition? Java 8 Predicates to the rescue! The Predicate interface is a functional interface. It has a method called the test method as shown below:

@FunctionalInterface
public interface Predicate<T> {

    boolean test(T t)

}

where t is the parameter that needs to pass a test. If it passes then it returns true else false

How do we use it?

This is done using lambda expressions. Right side represents the condition which in a lambda expression and then assign the condition to a Predicate reference.

How does it help?

Concentrate on the if conditions used in the code above and use a lambda expression instead.

All men: 

Predicate<Person> allMen = person -> person.getGender() == Gender.MALE;

getPeopleWithFilter(persons,allMen);

All women:

Predicate<Person> allWomen = allMen.negate();

getPeopleWithFilter(persons, allWomen);

All US Citizens:

Predicate<Person> usaCitizenship = p -> p.getCitizenship() == Citizenship.USA;

Get all men who are US citizens, done by combining predicates:

getPeopleWithFilter (persons, allMen.and(usaCitizenship));

Age above 35 years:

Predicate<Person> ageAbove35 = p -> p.getAge()  > 35;

Get all men who are US citizens and above the age of 35:

getPeopleWithFilter (persons, allMen.and(usaCitizenship).and(ageAbove35));

Now, all the methods written above can simply be deleted and replaced with:


private List<Person> getPeopleWithFilter(List<Person> persons, Predicate<Person> filter)
{
List<Person> people = new ArrayList<>();
for(Person person : persons){
if(filter.test(person) {
people.add(person);
}
}
return people;
}

You say to yourself, create new list, iterate, single if condition using predicate. Hmm, can we do something more? Can we get rid of the creation of the new list, the for loop ?

More refactoring:


private List<Person> getPeopleWithFilter(List<Person> persons, Predicate<Person> personFilter)
{
return persons.stream()
.filter(personFilter)
.collect(Collectors.toList());
}

The filter method on the stream accepts a Predicate which internally returns true or false. If it returns true, it gets collected into the list else it gets filtered out.

This code has no temporary creation of a new list during the stream or filter calls, has no explicit iteration, no repetitive if conditions!

The code looks cleaner, more expressive and maintenance of this code is easier. Using Lambdas and the Predicate interface we can do powerful things.The next time your manager walks up to you with a new requirement, write a simple Predicate and call the method above. It is 6 pm already, time to check in and leave for the coffee date, this time make sure you reach before time, carry some flowers or may be a card too which says “You look beautiful like my code”!

Moral of the story: Love thy code!

One thought on “Java 8 Predicate Interface using Lambdas”

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: