Taming verbosity in the Java CMA SDK 2.0: Method chaining and fluent interfaces

Blogpost java2.0

Java folks, we have some good news for you — the Contentful Java CMA SDK 2.0 is here! It supports one of our most recent features, Direct Files Upload, something that we know will make a lot of users happy. It’s also the best version ever™ of the SDK, with bugfixes and more goodies under the hood. It’s the Java that you know and love, with powerful features and as verbose as ever… Or is it?

For all its great features, Java is undoubtedly rather verbose. Oftentimes this is good, because it helps you spot errors and build better apps, but when building this SDK I realized that there was a trick I could adopt which could save me a bunch of keystrokes and make the code more readable. Interested? Without further ado, let’s take a look at method chaining!

Method what?

Have you ever encountered a chain of methods like the following?

1
someObject.setFoo(123).setBar("Mario");

For chaining of setter methods, we will digress a bit from the known paradigm of setters, since our's will need to return something. In the above snippet setBar needs to be executed on something, so setFoo needs to return it. For reasons we will discuss further down, it is useful to have the return type of each setter in a chain return the same type, in this case whatever someObject's type is.

An implementation of the setFoo setter method can look like this:

1
2
3
4
5
6
7
public Chain setFoo(int value) {
  if (value > 0) {
    this.value = value;
  }

  return this;
}

This implementation checks the incoming value and assigns it to an object field, just as a regular setter would. Interestingly, it returns also the this object. This is the meaty part of the chain: This way you could call setFoo (and hence all methods from the instance) again.

1
someObject.setFoo(123).setBar("Mario");

A quick note for readability: Since we are doing several things in a chain, I recommend rewriting the sample snippet and execute every setter in a separate line, preceding it with the dot accessor:

1
2
3
someObject
  .setFoo(123)
  .setBar("asdf");

This way, the readability is ensured, and in case of an unexpected exception the stack trace points directly to line the failing setter was used, and not the complete chain!

Method chaining makes the source code more glanceable and less convoluted especially when dealing with multiple sets of chains. Let’s take a look at these two snippets:

Without chaining:

1
2
3
4
5
6
7
8
Chain inner = new Chain();
inner.setFoo(123);
inner.setBar("asdf");

Chain outer = new Chain();
outer.setFoo(123);
outer.setBar("asdf");
outer.setInner(inner);

With chaining:

1
2
3
4
5
6
7
Chain outer = new Chain()
.setFoo(123)
.setBar("asdf")
.setInner(new Chain()
  .setFoo(43)
  .setBar("inner Chain")
);

Both snippets are setting values into a cascading structure. The first one uses a non chaining approach, so we first need to define the values to be set in the outer object and then call the setter for each of them. On the other hand, in the second snippet, we can define the inner object in place and set all the values where needed. This has the positive side effect of not needing to name the inner object, therefore we don't need to clutter the namespace with just another temporary variable name.

Analysis

Now that we took a glance into what a chain is and how to use one, let’s take a look at what we can use it for. In our Java CMA SDK, all resources are using chaining to make the code more readable, especially once you have to include changes, or create new resources. Let’s take a look at a practical use case, where we are uploading an assets:

1
2
3
4
5
6
7
8
9
CMAAsset asset = new CMAAsset()
  .setId("assetid")
  .setSpaceId("spaceid")
  .setVersion(1);

client
  .assets()
  .async()
  .create("<spaceId>", asset);

In the above example we do two things: first, we create a new asset and use our chaining setters, and then we are pushing this asset to Contentful. The first part should look familiar to you by now, and although the second one looks similar, it’s not actually using chaining setters, but rather a fluid interface. The difference between those two is that the fluid interface can and is expected to return different types for each step of the chain. So client.assets() does return an instance of the assets module, and not an object of the same type as the client's.

Why are we taking a look at it? Because you can also combine both, chaining setters and fluid interfaces:

1
2
3
4
5
6
7
8
9
10
11
CMAAsset asset = new CMAAsset()
  .setId("assetid")
  .setSpaceId("spaceid")
  .setVersion(1)
  .getFields()
  .localize("en-US")
    .setFile(new CMAAssetFile()
    .setContentType("image/jpeg")
    .setUploadUrl("https://www.nowhere.com/image.jpg")
    .setFileName("example.jpg")
  );

Here we are using the setters with a fluid interface. The .getFields() and .localize() methods are returning different types. This approach has one problem: it is not easy to tell that after the call to .getFields() you cannot call .setVersion() again, since this setter does not exists on the return type of the .getFields() method. This is also where we find one of the problems with chaining and fluent interfaces: readability of the chain can be difficult, if the return types of the chain change too much. So a recommendation would be to keep the different return types to a minimum to not confuse you in a couple of weeks — or your co-workers now!

Type Hierarchy

Let’s take a look into inheritance and how it works with chaining. Let’s assume for example that you have a super type called CMARessource which defines common field to be set on all the resources, also for a subclass, CMAAsset. Take a look at this snippet from Contentful's CMAAsset class:

1
2
3
4
5
6
public class CMAAsset extends CMAResource {
  // …
  @Override public CMAAsset setId(String id) {
    return super.setId(id);
  }
}

CMAResource implements the setId method like this:

1
2
3
4
    public <T extends CMAResource> T setId(String id) {
      getSystem().setId(id);
      return (T) this;
    }

The keen eye might have noticed the difference in the return type of the methods: CMAAsset vs <T extends CMAResource>. The nice thing is that with this construct CMAAsset is extending CMAResource and also redefines the return type, which makes the chain limited to only one type: CMAAsset. Without overwriting the setter, it would be taken from the CMARessource class and would not return a CMAAsset, which would make accessing CMAAsset specific methods impossible without a typecast.

To summarize, this allows us to have a construct like:

1
2
3
new CMAAsset()
  .setId("assetid")
  .getFields();

where .getFields() called in CMAAsset and not in CMAResource.

Conclusions

Chaining setters is a nice trick, but is to be used with care. If you let a setter return the this object, you can chain them together and save some mental overhead. As always, too much of a good thing can be a bad thing, so overusing this can result in more confusing code. My suggestion is to adopt this paradigm where it makes sense, and keep changes of return types in a fluent interface to a minimum.

If you want to see this pattern in action, take a look at our Java CMA 2.0.0 SDK, and let us know what you think about it. As always, you can head to the docs to find out more about it, or explore the source code (and open issues, should you find any) on Github.

Happy coding!

Blog posts in your inbox

Subscribe to receive most important updates. We send emails once a month.