Effective Java! Favor the Use of Standard Functional Interfaces

Kyle Carter
3 min readJun 9, 2021

With lambdas as part of the Java language more possible implementations have opened up. Where before we may have used an alternate pattern such as maybe the Template Method Pattern where a subclass overrides a method to specialize the behavior of the superclass, we can now use a factory method that takes a lambda that serves as the specialization function.

Let’s look at another example, LinkedHashMap. This can serve as a cache by overriding the removeEldestEntry which gets invoked on each put operation with the oldest item and which, when it returns true, removes the oldest entry. For example, if we wanted to limit our map to 100 entries we could write something like:

protected boolean removeEldestEntry(Map.Entry<K,V> eldest) {
return size() > 100;
}

This method works fine but if it was built today it may be cleaner to pass a lambda to fulfill this purpose. So let’s think through what this map would need to take. It would of course take Map.Entry<K,V> just like our removeEldestEntry method. It also needs a reference to the map itself since it calls the size method. Finally we need to consider the return type which in this case would be boolean. So putting this all together we would get:

@FunctionalInterface
interface EldestEntryRemovalFunction<K,V> {
boolean remove(Map<K,V> map, Map.Entry<K,V> eldest);
}

This interface will work great, it’s exactly what we need. The question is is this necessary? Java provides us with forty-three functional interfaces in the java.util.function package which covers many, many use cases. I don't think anyone expects you to memorize all the interfaces but there are certain building blocks that if we understand we can derive what interfaces are available to us.

Let’s dive into these building blocks. There are five main classes of functional interfaces. Operator interfaces (which there are Unary and Binary versions that take one and two arguments respectively) which take and return the same argument type. Predicate which takes an argument and returns a boolean. Function which arguments and return type differ. Supplier which takes no arguments and returns a value. Consumer which takes arguments and returns nothing.

There are three variants of each of these that operate on int, long, and double, for example IntPredicate which takes an int and returns a boolean. We also have Function interfaces that…

--

--

Kyle Carter

I'm a software architect that has a passion for software design and sharing with those around me.