IT Blog & Photo Gallery

Applied Functional Refactoring in Java

November 17, 2017

Since the release of Java 8, a bit of functional elegance has come to Java and many have already written about it. Still I haven't yet read anything on using those new elements to improve the structure, readability and maintainability of existing software. As I've had the opportunity do some refactoring in the past weeks using exactly those functional elements, I'll let you take part in my experience.

Just a little bit of context upfront: the project on which I did the refactoring was Spring Boot based and about two years old. It did already use some functional elements, for example the new streaming-API. More advanced features were just sparely used, so there was a lot of space for introducing more functional elements.

Repeated conditional operations

For most projects that have domain entities with a large number of fields, there might be some mapping, state checking, or similar operation that involves evaluating structurally identical conditions for each field and executing some functionality, e.g. setting a new field value. Without the usage of functional elements, such code might look like this:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
public List<String> findChangedFields(Entity existing, Entity updated) {
    List<String> changedFields = new ArrayList<>();
    BigDecimal existingFieldA = existing.getFieldA();
    BigDecimal updatedFieldA = updated.getFieldA();
    if (existingFieldA == null && updatedFieldA != null
        || existingFieldA != null && updatedFieldA == null
        || existingFieldA.compareTo(updatedFieldA) != 0) {
        changedFields.add("FieldA");
    }
    BigDecimal existingFieldB = existing.getFieldB();
    BigDecimal updatedFieldB = updated.getFieldB();
    if (existingFieldB == null && updatedFieldB != null
        || existingFieldB != null && updatedFieldB == null
        || existingFieldB.compareTo(updatedFieldB) != 0) {
        changedFields.add("FieldB");
    }
    // repeat...
    return changedFields;
}

Listing 1: Initial code without functional elements

If you’d find such a piece of code in a real project, you might take a short moment to actually realize that the same kind of check is done over and over again. Even without the use of functional elements there might be some refactoring possible, but definitely not as much as with them. To make more explicit what’s done, we’ll first separate the condition checking from the fields that are being checked using a tiny inner class. It’ll take both Entity instances as constructor arguments and its only method will take a method reference to a field and return the condition result as a boolean. After this refactoring, the above code fragment might look like this:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
public List<String> findChangedFields(Entity existing, Entity updated) {
    List<String> changedFields = new ArrayList<>();
    FieldChecker check = new FieldChecker(existing, updated);
    
    if (check.isChanged(Entity::getFieldA) {
        changedFields.add("FieldA");
    }
    if (check.isChanged(Entity::getFieldA) {
        changedFields.add("FieldB");
    }
    // repeat...
    return changedFields;
}

private static class FieldChecker {
    private final Entity existing;
    private final Entity updated;

    // Constructor setting the two fields
    
    private boolean isChanged(Function<Entity, BigDecimal> accessor) {
        BigDecimal existingFieldB = accessor.apply(this.existing);
        BigDecimal updatedFieldB = accessor.apply(this.updated);

        return existingFieldB == null && updatedFieldB != null
            || existingFieldB != null && updatedFieldB == null
            || existingFieldB.compareTo(updatedFieldB) != 0;
    }
}

Listing 2: First iteration, using an inner class and method references

The benefits of this refactoring might not be as obvious as it gets when there are not two but twenty fields that are being checked, still the findChangedFields method implementation is much more revealing than the first version and the added inner class is still very small. Of course you’ll be able to add new methods to the inner class, if there are other data types that need to be checked as well.

But wait, there’s more: now that the refactoring is done, you’re still left with a number of repeated if statements and if you think about it, there is actually an implicit mapping being done: from a changed field value to a string containing the changed fields name.

Implicit mappings

I call it implicit mapping, when some kind of mapping done, but there is no actual map of what to map and structure and data are mixed within the code. Real functional languages like Haskell are really good in separating structure and data, so let’s try if Java can do that as well. First, we’ll need the actual map to operate on. Because we don’t want to instantiate this mapping every time we use it, we’ll create it as a static final class field. This is the single part that isn’t as nice as I’d like it to be, because we’ll need a static-Block to fill this map. Help might be found in libraries like Vavr, that bring new Maps with them that allow initialization inside the constructor or chained builders. When we have this mapping, all that is left to do is iterating the map, evaluating the condition for every entry, do the mapping for all matched elements and add each result to our list. Of course, this can be done with a lovely little Stream:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
private static final Map<Function<Entity, BigDecimal>, String>
    changedFieldMapping = new HashMap<>();

static {
    changedFieldMapping.put(Entity::getFieldA, "FieldA");
    changedFieldMapping.put(Entity::getFieldB, "FieldB");
}

public List<String> findChangedFields(Entity existing, Entity updated) {
    FieldChecker check = new FieldChecker(existing, updated);
    
    return changedFieldMapping.keySet().stream()
        .filter(check::isChanged)
        .map(changedFieldMapping::get)
        .collect(Collectors.toList());
}
// FieldChecker stays untouched

Listing 3: Second iteration, using explicit mapping and streams

Now we have our explicit mapping that states which fields should be mapped to which name and clearly separated from it, in the original findChangedFields method, we iterate this mapping, filter for all fields that are changed, map to their name and collect a list of all those names of changed fields. At this state we might simply stop and settle with the results, especially if there are fields of multiple data types being checked.

Single method classes

But in this example, we’re left with a class containing just a constructor and a single method. As Jack Diederich described in his talk Stop writing classes, those might be replaced by plain functions. Because our constructor takes two arguments, we have to use a lambda expression to achieve this goal.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// mapping stays untouched

public List<String> findChangedFields(Entity existing, Entity updated) {
    
    return changedFieldMapping.keySet().stream()
        .filter(changedFields(existing, updated))
        .map(changedFieldMapping::get)
        .collect(Collectors.toList());
}

private Predicate<Function<Entity, BigDecimal>> changedFields(
        Entity existing,
        Entity updated
    ) {
    return accessor -> {
        BigDecimal existingFieldB = accessor.apply(existing);
        BigDecimal updatedFieldB = accessor.apply(updated);

        return existingFieldB == null && updatedFieldB != null
            || existingFieldB != null && updatedFieldB == null
            || existingFieldB.compareTo(updatedFieldB) != 0;
    };
}

Listing 4: Third iteration, replaced inner class by lambda

What’s now leftover is pure functional joy. The signature of our new method might be a bit complex at first, but in the end it’s just a predicate that evaluated a given function for the two given Entity arguments. A Predicate differs from a Function in that it always returns an unboxed boolean, so it can be evaluated as a conditional without doing an additional null check for the returned value. Although I prefer this approach to the inner class, they’re both perfectly fine, especially if - speaking for this example - there are multiple data types being checked, as each type would need it’s own lambda returning method. Also developers not so used to functional programming might stumble at that point, because it’s easy to confuse the when and where which parameter of the changedFields method is passed.

Overuse of functional interfaces

While there are a lot of good usages for lambdas, it’s also possible to misuse or overuse them. A type of over usage that I’ve seen are utility classes containing no actual methods but variables of type Function, Supplier, BiFunction, Consumer, or any other kind of functional interface. This might seem reasonable when those functions are only or primarily used as arguments to stream methods or other functional APIs, but otherwise there is no real benefit by doing so. If you want to use those functions directly, you’ll always have to append an additional .apply, .consume, or such. That alone might not be too bad, but the declaration of those functions is also and even more unnecessarily long and cluttered. To show you what I mean, let’s take an example mapping function that gets the credentials for an user object:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// Misused functional interface
public static final Function<User, Credentials>
    toCredentials = User::getCredentials;

// Conventional method
public static Credentials getCredentials(User user) {
    return user.getCredentials();
}

// exemplary usage:
void someMethod() {
    // ...
    credentials = users.stream()
        .map(Util.toCredentials)
        .collect(Collectors.toList());

    Util.toCredentials.apply(someUser);
    
    credentials = users.stream()
        .map(Util::getCredentials)
        .collect(Collectors.toList());

    Util.getCredentials(someUser);
}

Listing 5: Overuse of functional interfaces

I have to admit that this is a terrible example to some degree, because the whole function could easily be replaced by a single method reference instead. Still my point holds, that the conventional java method is better readable and it does not introduce any restrictions compared to the Function-variable. Also the usage of both (at least for me) looks better for the conventional method.

Conclusion

The functional elements of Java 8 are nice to refactor existing programs and reduce their complexity. Nonetheless it’s not always a good idea trying to use those elements everywhere possible. So from my view, the answer to the question “Should I do more functional in Java?” is a clear “it depends”.