Two 〈Foo〉s Walk into a 〈Bar〉: Featuring the Highest Angle Bracket–to–Word Ratio Among Site Titles

Static Factory Methods Effective Java #1

Book Summary

Static factory methods can:

  • have names
  • control when new instances are created
  • return any subtype of their return type
  • reduce the need to specify type parameters

Additional Points

Static factory methods can assist in safe publication and can allow for selective type bounds.

Safe Publication

Sometimes you want to create an object and publish a reference to it at the same time. Sometimes you publish the reference where other threads can see it. The obvious approach is to handle it all in the constructor:

public final class Request {
  /** A request from some point in the past. */
  public static Request sampleRequest;

  private final Query query;
  private final User user;

  public Request(Query query, User user) {
    this.query = query;
    this.user = user;

    sampleRequest = this;
  }

  public Query getQuery() { return query; }
  public User getUser() { return user; }
}

Notice that sampleRequest is not declared volatile. The intent is to give up the guarantee that all threads will see the most recent value in favor of the speed of a non‐volatile write. And it would work if only the code were slightly different:

public final class Request {
  /** A request from some point in the past. */
  public static Request sampleRequest;

  private final Query query;
  private final User user;

  public static Request create(Query query, User user) {
    Request request = new Request(query, user);
    sampleRequest = request;
    return request;
  }

  private Request(Query query, User user) {
    this.query = query;
    this.user = user;
  }

  public Query getQuery() { return query; }
  public User getUser() { return user; }
}

Readers of an object published by the former code can behave nondeterministically. But the latter code, by waiting until after the constructor to publish the reference (and by containing no non‐final fields), takes advantage of a feature of the memory model: the end‐of‐constructor "freeze." This freeze ensures that the object appears fully initialized (and thus immutable) to other threads. Read Jeremy Manson's article on immutability, and see also Java Concurrency in Practice pg. 41–42.

What does this esoteric knowledge gain you? Very little. The reason is that examples like the above are rare. In most cases, the act of publishing the reference is a volatile write or another synchronization action, such as adding the reference to a thread‐safe list of listeners. Such actions guarantee that all steps performed before the publishing step are visible to all threads reading the reference. The end‐of‐constructor freeze is superfluous.

All this said, if you depend on others to write defensive code and they depend on you, you'll have problems. Incomplete object initialization can be a security vulnerability: An uninitialized "immutable" object might appear to contain an innocuous request during security checks, only to transform into a malicious one before execution. Maybe spawning 0 processes is acceptable but 1,000,000 is not, or maybe 0 is a valid seed in a crypto algorithm but 1,000,000 is not.

Selective Type Bounds

The Problem

When parameterizing a class, I can choose to set bounds on the type parameter. For example, EnumSet has type parameter <E extends Enum<E>>. Or I can leave it unbounded: HashSet has type parameter E.

Bounded or unbounded: What other options do I need? Consider TreeSet. TreeSet instances divide into two groups: those with a Comparator and those without. When the caller provides a Comparator, the TreeSet can contain any kind of element supported by the Comparator. Since I can define a Comparator for any type that I like, I can create a TreeSet<List>, a TreeSet<Rectangle>, or any other type, so long as I provide an appropriate Comparator. On the other hand, when the caller does not provide a Comparator, the TreeSet can only contain elements that implement Comparable (specifically, elements that are Comparable to one another). A TreeSet<List> without a Comparator will throw exceptions when it is used.

The class's type parameter alone cannot provide us with the flexibility to solve this problem. The result is two kinds of TreeSet constructors. The first takes a Comparator. As it should, it allows the caller to fill in any type for the type parameter, provided that it's compatible with the given Comparator. The second type of constructor takes no Comparator argument, yet it, too, lets the client fill in any type for the type parameter. This is broken, as it should allow only classes that implement Comparable.

The Solution

The fix is to remove TreeSet's constructors in favor of static factory methods:

<E> TreeSet<E> newTreeSet(Comparator<? super E> comparator);
<E extends Comparable<E>> TreeSet<E> newTreeSet();

As before, clients that provide a Comparator can create TreeSet instances for non‐Comparable types. But now an attempt to create a TreeSet instance for a non‐Comparable type without supplying a Comparator will fail at compile time.

Another option is to do away with the no‐arg constructor entirely and to force callers to pass Comparators.naturalOrder(), a Comparator<C> for any C that extends Comparable, to the remaining constructor. It won't take many calls to new TreeSet<T>(Comparators.<T>naturalOrder()) to suggest that a static factory method is in order.

Java trivia: Parameterized constructors are permitted:

private <T> MyClass() { ... }

However, I have not found a way to use parameterized constructors to produce an equivalent solution.

Applications

TreeSet and other SortedSet implementations are the obvious example. I could also imagine a scenario in which a class can serialize either (a) a class that knows how to convert itself to a string or (b) an arbitrary object plus an object that knows how to convert it to a string. These cases are analogous the SortedSet case: Some objects can perform the necessary tasks themselves, while others require an external converter. Static factory methods allow us to check uses of both types at compile time.

<E> Serializer<E> newStringSerializer(Stringifier<E> stringifier);
<E extends Stringifiable> Serializer<E> newStringSerializer();

It's also possible to permit only a fixed set of type parameters:

Serializer<Integer> newSerializer();
Serializer<String> newSerializer();