Java of() static factory method idiom

The of() static factory method idiom, used to instantiate types, has become common both within the JDK and third party libraries.

Static factory methods have been around since the earliest days of Java, but back then the idiom was to name these methods valueOf, for example:

Over time a preference has developed for using the slightly terser of() form.

EnumSet was one of the first types (introduced in JDK 1.5) to prefer the shorter of form.

Over time the better readability of code that uses of() led to that form taking precedence. With the introduction of functional programming techniques in JDK 8, along with support for static methods in interface types, usage of the of() idiom became widespread across the JDK, for example:

Benefits of static factory methods

Joshua Bloch’s Effective Java states:

Item 1: Consider static factory methods instead of constructors The traditional way for a class to allow a client to obtain an instance is to provide a public constructor. There is another technique that should be a part of every programmer’s toolkit. A class can provide a public static factory method, which is simply a static method that returns an instance of the class.

(Incidentally, Joshua is one of the authors of the EnumSet type).

Joshua lists many benefits to using static factory methods, but to summarize they boil down to using a constructor introduces strong coupling between the calling code and the instantiated type in two ways:

  • The new operator means a new instance of the type must be created.
  • Only the exact type named in the new expression can be instantiated.

When a constructor invocation is replaced by a static factory method invocation, both these hard constraints can be relaxed.

Avoiding instantations

The static factory method is no longer obliged to create a new instance, which can provide optimization opportunites, for example BigDecimal.valueOf(long) has code like the following:

1
2
3
4
5
    public static BigDecimal valueOf(long val) {
        if (val >= 0 && val < ZERO_THROUGH_TEN.length)
            return ZERO_THROUGH_TEN[(int)val];
        ...
    }
  • The implementation recognizes that certain values are more frequently instantiated than others, and keeps a cache of the first 10 values, returning a cached BigDecimal instance for such values.

Avoiding coupling to a specific type

For many reasons it can beneficial to avoid explicitly specifying the type to be instantiated, particularly in long lived code which may need to be enhanced and refactored over a long period of time.

For example the implementation of EnumSet.of(E) optimizes for the number of elements in the Enum, if there are 64 or less it instantiates an instance of RegularEnumSet (which tracks the elements in a single 64 bit long value). If there are more than 64 elements then it uses the slightly less efficent JumboEnumSet.

Centralizing the instantation of the type in a static factory method makes it much easier to change what specific type is instantiated, rather than having to change thousands of new statements all over a code-base.

Ⓗ Home   Ⓑ Blog   Ⓐ About