Radical Java

Don't tell me what to make, tell me how to make it

Let’s make a new object:

Person person = new Person();

Simple, isn’t it. At least that’s how your basic 101 Java course teaches you about creating new objects, but unfortunately it’s rarely as simple as that. All kinds of things get in the way, which is probably why so much of the literature about design patterns in Java concerns itself with object creation. At some point in time you’ll probably want to delegate the creation of an object to a generic piece of code which is not so rigid about type.

Today I’d like to talk about something that I’ve seen in countless libraries and frameworks, especially those concerned with object serialization and deserialization. A common requirement of these is that your many of your classes must have a no-arg constructor; for example in Avro, Jackson (when not using newer @JsonCreator features) and Hadoop to name a few, and the latter of these gives us a hint as to why. They all want to be able to use the Java reflection API on an your Class object to invoke a constructor and obtain a new instance.

Lets consider a framework which creates many instances of an Application and run them in parallel.

public class Application implements Runnable {
    public void run() {

    }
}

/* ... */

public class MyFramework {
    public static <T extends Runnable> void runMany(int nInstances, Class<T> appClass) {
        List<T> instances = new ArrayList<T>();
        try {
            for (int i = 0; i < nInstances; i++) {
                instances.add(appClass.getConstructor().newInstance());
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        runInParallel(instances);
    }
}

/* ... */

MyFramework.runMany(10, Application.class);

This method of reflective construction is:

  1. Unsafe, because the compiler doesn’t know if your class has a no-arg constructor at compile time and subsequently may throw exceptions at runtime.
  2. Inflexible, because you can’t add a no-arg constructor to a class you don’t control and you may want to use another method to create your object, such as a factory method or pre-filled constructor call.
  3. Indirect - introspection gives the JVM more work to do and can slow your application down.

So why is it so common?

The fundamental limitation of the Java OO model in this context is that you cannot define polymorphic methods at the class level, only at the object level, so there’s no such thing as expressing in code the type bound “T is a class with a no-arg constructor”, so you must do it by convention and introspection instead.

Factories

One way round this is to create a factory - an object for creating objects - which conforms to an interface, but this pushes the burden of complexity and boilerplate onto the user of such an API rather than the framework or library author. For example, I could rewrite my code above as follows:

interface Factory<T> {
    T create();
}

public class MyFramework {
    public static <T extends Runnable> void runMany(int nInstances, Factory<T> appFactory) {
        List<T> instances = new ArrayList<T>();
        for (int i = 0; i < nInstances; i++) {
            instances.add(appFactory.create());
        }
        runInParallel(instances);
    }
}

/* ... */

public class ApplicationFactory implements Factory<Application> {
    public Application create() {
        return new Application();
    }
}
/* ... */

MyFramework.runMany(10, new ApplicationFactory());

This is much safer and more flexible than the reflective version, but it requires the client to do a lot more work to use it (namely implement ApplicationFactory), which is probably not what you want either, and why so many library authors have opted for the former approach.

Suppliers to the rescue

Luckily, the team behind Java 8 were aware of this unpleasant tradeoff too and returned with the stock Supplier<T> interface, which looks very much like the Factory<T> interface I just defined. At first this doesn’t look like much, but Java 8 also gives us extremely convenient methods to implement so-called “functional interfaces” like these, namely lambda expressions and method/constructor references. This means we can construct a solution which is functionally equivalent to creating a factory, but with no extra burden on the client to do extra work - fantastic news for just about everyone. Let’s have a look at our app using a Supplier and constructor reference instead.

public class MyFramework {
    public static <T extends Runnable> void runMany(int nInstances, Supplier<T> appFactory) {
        List<T> instances = new ArrayList<T>();
        for (int i = 0; i < nInstances; i++) {
            instances.add(appFactory.get());
        }
        runInParallel(instances);
    }
}

/* ... */

MyFramework.runMany(10, Application::new);

This version has the simplest code on both sides of the client/framework boundary, and is safe and flexible as well. For example, if you want to use a factory method with args to create the object you can do this instead:

MyFramework.runMany(10, () -> Application.create("some", "args"));

What does this mean for me?

If you are writing a component and you need at any point to create objects whose type is dictated by the client, wherever possible you should consider accepting Supplier<T> instead of asking for Class<T> then or an empty T object.