As software developers, we behave like children. When we see shiny new things, we just have to play with them. That’s normal, accepted, and in general, even beneficial to our job…​ up to a point.

When Java started to provide annotations with version 5, there was a huge move toward using them. Anywhere. Everywhere. Even when it was not a good idea to. But it was new, hence it had to be good. Of course, when something is abused, there’s a strong movement against it. So that even when the usage of annotations may make sense, some developers might strongly be against it. There’s even a site about that (warning, trolling inside).

Unfortunately, we didn’t collectively learn from overusing annotations. With a lot of companies having migrated to Java 8, one start to notice a lot of code making use of lambdas like this one:

List < Person > persons = ...; persons . stream (). filter ( p -> { if ( p . getGender () == Gender . MALE ) { return true ; } LocalDate now = LocalDate . now (); Duration age = Duration . between ( p . getBirthDate (), now ); Duration adult = Duration . of ( 18 , ChronoUnit . YEARS ); return age . compareTo ( adult ) > 0 ) { return true ; } return false ; }). map ( p -> p . getFirstName () + " " + p . getLastName ()) . collect ( Collectors . toList ());

This is just a stupid sample, but it gives a good feeling of the code I sometimes have to read. It’s in general longer and even more convoluted, or to be politically correct, it has room for improvement - really a lot of room.

The first move would be to apply correct naming, as well as move the logic to where it belongs to.

public class Person { // ... public boolean isMale () { return getGender () == Gender . MALE ; } public boolean isAdult ( LocalDate when ) { Duration age = Duration . between ( birthDate , when ); Duration adult = Duration . of ( 18 , ChronoUnit . YEARS ); return age . compareTo ( adult ) > 0 ; } }

This small refactoring already improves the readability of the lambda:

persons . stream (). filter ( p -> { if ( p . isMale ()) { return true ; } LocalDate now = LocalDate . now (); return p . isAdult ( now ); }). map ( p -> p . getFirstName () + " " + p . getLastName ()) . collect ( Collectors . toList ());

But it shouldn’t stop there. There’s an interesting bia regarding lambda: they have to be anonymous. Nearly all examples on the Web show anonymous lambdas. But nothing could be further from the truth!

Let’s name our lambdas, and check the results:

// Implementation details Predicate < Person > isMaleOrAdult = p -> { if ( p . isMale ()) { return true ; } LocalDate now = LocalDate . now (); return p . isAdult ( now ); }; Function < Person , String > concatenateFirstAndLastName = p -> p . getFirstName () + " " + p . getLastName (); // Core persons . streams () . filter ( isMaleOrAdult ) . map ( concatenateFirstAndLastName )

Nothing mind-blowing. Yet, notice that the stream itself (the last line) has become more readable, not hidden behind implementation details. It doesn’t prevent developers from reading them, but only if necessary.