eHarmony Engineering logo

Design Pattern: Private Type Parameters, an Application of Type Currying for Java

Rick Warren

July 16, 2014

I’ve recently noticed myself applying the following design pattern. Then I started noticing it in libraries that I use. It’s better known in Functional circles; I haven’t previously seen a formal description for Java developers.

Just as functions can be curried by accumulating and encapsulating object parameters, types can be curried by accumulating and encapsulating their generic type parameters. The particular application of type currying that I’ve seen most often, which I’ve come to think of as the Private Type Parameter pattern, applies in the case where a type “Outer” requires parameterization with another type “INNER” at initialization time, but thereafter uses that type parameter only within its internal implementation. Outer does not expose INNER in its public API post-initialization. (For example, Outer may accept multiple constructor parameters, where one parameter later becomes an input to the other, or produces a value to be used by the other.) Therefore, in the interest of encapsulation, clients of already-initialized Outer instances should not have to be aware of INNER.

Consider the following example:

/** Assume there are many subtypes: Dog, Cat, Hamster, etc. */
public interface Pet {
  /** Make your pet nice and clean. */
  void groom();
}

/**
 * Given a Class representing a specific subtype of Pet, construct or
 * otherwise provide an instance of that Pet.
 */
public interface PetProvider {
  <P extends Pet> P getPet(Class<P> petClass);
}

/** Lazily access a particular Pet and groom it. */
public class PetGroomer<P extends Pet> {
  private Class<P> petClass;
  private PetProvider provider;

  public PetGroomer(Class<P> petClass, PetProvider provider) {
    this.petClass = petClass;
    this.provider = provider;
  }

  public void groomPet() {
    P pet = provider.getPet(petClass);
    pet.groom();
  }
}

As the code above is expressed, anyone using a PetGroomer will need to include a type parameter: PetGroomer<Dog>,  PetGroomer<GuineaPig>, etc. But doing so is a waste, because all I can do with my PetGroomer is call groomPet(), which doesn’t change based on the parameter. I might as well pass all PetGroomer instances around as PetGroomer<?>, in which case, why bother with the parameter at all?

The solution is to replace the public constructor with a factory method, and encapsulate the type parameter in a nested subclass:

public abstract class PetGroomer {
  public static <P> PetGroomer create(
      Class<P> petClass,
      PetProvider provider) {
    return new PetGroomerImpl();
  }

  public abstract void groomPet();

  private class PetGroomerImpl<P> extends PetGroomer {
    private Class<P> petClass;
    private PetProvider provider;

    public PetGroomerImpl(Class<P> petClass, PetProvider provider) {
      this.petClass = petClass;
      this.provider = provider;
    }

    @Override
    public void groomPet() {
      Pet pet = provider.getPet(petClass);
      pet.groom();
    }
  }
}

Now, the type parameter is properly encapsulated within the implementation, and clients can simply refer to the undecorated PetGroomer type.

Lest you feel that my example is contrived, here’s an example from Google’s Guava (simplified slightly for brevity).

class FunctionComposition<A, B, C> implements Function<A, C> {
  private Function<B, C> g;
  private Function<A, ? extends B> f;

  public FunctionComposition(
      Function<B, C> g,
      Function<A, ? extends B> f) {
    this.g = g;
    this.f = f;
  }

  @Override</span>
  public C apply(A a) {
    return g.apply(f.apply(a));
  }
}

The public API depends on type parameters A and C, while the implementation additionally depends on B.

Guava also hides construction behind a factory method. FunctionComposition is a private nested class of the Functions utility class, which also contains this method:

public static <A, B, C> Function<A, C> compose(
    Function<B, C> g,
    Function<A, ? extends B> f) {
  return new FunctionComposition<A, B, C>(g, f);
}

In summary, here are the characteristics of the pattern:

  1. A type Outer,
  2. …whose private implementation depends on a generic type parameter INNER,
  3. …but whose public API does not.

To implement this pattern in Java while maintaining proper encapsulation:

  1. Make Outer abstract and keep it free of the type parameter INNER.
  2. Attach that type parameter instead to a public static factory method, which returns an instance of a generic non-public subclass of Outer, e.g. OuterImpl<INNER>.

You’ll encounter variations on this pattern:

  • Outer may be either an interface or an abstract class.
  • Outer may define the factory method itself, or that job may fall to a third type. (If Outer is an interface, as in the Guava example, that job must fall to a third type.)
  • Outer may or may not have additional, public, type parameters.

What other examples of type currying have you implemented or encountered in Java or another OO language? Were they implemented in the same way as I’ve described here?