Using Java 8 Optional and Functional interfaces to reduce boilerplate

A few years back I got curious about Scala, and fiddled with it a bit – becoming, in the process, very excited about the functional programming style.
(this is available in Python too – but it’s not as well-developed IMO, and anyway Python is best-suited to short-scope scripting -even though I make heavy use of its OOP abilities).

Java 8 is a good attempt at bringing the functional style to the JVM too, and I’m getting more and more used to it – still not quite as “native feel” as in Scala, but not too shoddy either.

I’ve been recently much vexed by some code I was writing that was not very DRY and required a lot of boilerplate, repetitive (and thus, error-prone) boring code; to wit, something like this::

         VersionedSrore vval = store.get(userId);

         if (vval != null) {
            PBData myData = PBData.newBuilder(vval.getValue())
                .addCard(ccard)
                .setLastUpdatedMillis(System.currentTimeMillis())
                .build();
            store.putVersioned(userId, vval.newValue(myData));
        } else {
            PBData myData = PBData.newBuilder()
                .setUserId(userId)
                .addCard(ccard)
                .setLastUpdatedMillis(System.currentTimeMillis())
                .build();
            store.putVersioned(userId, store.createInitialVersioned(myData));
        }

over and over again, very similar code repeated in various methods in the same class.

This typically occurs when dealing with Google Protocol Buffers and “versioned data stores” (such as LinkedIn’s Voldemort) that both deal generally with immutable objects, make heavy use of the Builder Pattern and require every store action to be effected via a “versioned value” (essentially, wrapping a Vector Clock object).

The (double) challenge here is that we might, or might not, have a value in the store, and depending on that we need to (a) use slightly different newBuilder() signatures and (b) slightly different, but very similar, store.put() methods.

I totally hated it – so I tried to figure out a way to factor it out in its own method::

private void transformAndUpdate(Long userId,
        Function transform) {

    PBData.Builder builder = Optional.ofNullable(store.get(userId))
        .map(versioned -> {
            // This builder factory method takes a "prototype" and
            // initializes all the known fields with the prototype's values.
            return PBData.newBuilder(versioned.getValue()); 
        })
        // This one, instead, creates an empty protobuf, with default values.
        .orElse(PBData.newBuilder().setUserId(userId));

    // Because this object is used in two places (that cannot be "collapsed")
    // we extract it to a temporary variable, to make the code slightly less 
    // verbose.
    PBData myData = transform.apply(builder)
          .setLastUpdatedMillis(System.currentTimeMillis())
          .build();

    store.putVersioned(userId,
        Optional.ofNullable(vval)
            .map(versioned -> { return versioned.newValue(myData); })
            .orElse(store.createInitialVersioned(myData)));
}

The transform functional takes the builder, applies whatever mutations are defined by the caller and returns the same (generally, but not necessarily) builder for further processing.
We simply note the update time and then build the protobuf.

Finally, we put back in the store the original (modified) “versioned” value we
had retrieved earlier, or a brand new one, if none was found.

Note the use of ofNullable() which will not throw even if vval
is null.

When you remove the comment lines and other formatting sugar, this is slightly less than 10 LOC, in ONE place, easy to update, maintain and improve, if need be.
(the code above can be further streamlined, but there’s a fine line between writing beautifully lean code and showing utter disregard for “the next guy”…)

With that, all we need now in every method that wishes to insert or update a record is essentially a one-liner that implements the Functional interface::

@Override
public void addUserCard(Long userId, PBCreditCard ccard) {
    try {
        transformAndUpdate(userId, (builder -> {return builder.addCard(ccard);}));
    } catch (BDObsoleteVersionException e) {
        LOG.error("Could not add '{}' to [{}] payment methods: {}", ccard.getCode(), userId, e.getMessage());
    }
}

The code above makes judicious use of Optionals, Functionals and the new Streaming interfaces.
It’s all very awesome, and makes me wonder: Java, what took you so long?

To be honest, I still quite dislike the fact that (unlike Python, C++ or Scala) one still needs to use the apply() method on a functional interface, instead of simply adding the operator ( ); however, if that’s the price to pay for preventing people from defining their own custom operators, well, that’s a damn small price to pay!

Advertisement

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: