Generic Constructors: What are They Good For?

Generics can be declared on types (classes and interfaces), methods, and constructors. Wait, constructors? That one threw me a little. I’ve never actually seen or used this feature. It makes logical sense to be able to declare a generic type on a constructor; after all, it’s just a special case of a method.

I could explain how to declare and use generic constructors in a rigorous and academic way, but that doesn’t give us a sense of if or whether it’s actually a useful feature. Instead, let’s see how and how often they’re used in the real world. That’s why we have open source! Let’s look for examples of generic constructors in some of the most popular Java projects and see if we can find any patterns.

Below is a list of all the projects I evaluated. I sourced most of them from the awesome-java Github project, a list of “awesome” Java projects and frameworks. I filtered the list based on a few criteria:

  • Does the project have 1,000 stars or more? This gives a rough proxy for popularity (or age) and usefulness. I’ve included a few projects that have less than 1,000 stars, however—such as the eclipse-collections—if I had a gut feeling that they may include usages of generic constructors (as it turned out, the eclipse-collections does not have any generic constructors).
  • Does the project have license compatible with the type of evaluation I want to make? Most are Apache 2.0 or MIT.
  • Is the project mostly Java source code? There were a few Scala-dominant projects in the awesome-java list I decided not to evaulate.
  • Is the source code hosted on Github? There are many great projects out there that are not hosted on Github (e.g., not all Apache Foundation projects have Github mirrors). However, projects on Github were the easiest to download in a systematic way.

I downloaded 187 projects, listed below in alphabetical order:

I downloaded all of these from Github, and put them into a folder called ./samples. Let’s get some rough statistics about our sample set. It includes:

  • 188,829 Java source files

    $ find ./samples/ -name "*.java" | wc -l
      188829
    
  • 29,184,117 lines in all of those Java source files (including comments,
    blank lines, etc.).

    $ find ./samples/ -name "*.java" -print0 | xargs -0 cat | wc -l
     29184117
    

In all of this code, surely we’ll find examples of generic constructors. I wrote a small Java project to parse the samples and look for declarations of generic constructors. You can find the source code on Github project, rare-generic-features.

Results

After running the above parsing code, the sample codebases contain:

  • 116 constructors with generics in 93 unique files.

From all of these use cases, there are three distinct patterns that emerge.

Pattern 1: Force parameter types to match

The first pattern ensures that the type of two or more parameters is the same. One good example is from the commons-lang project, in the ReflectionDiffBuilder.java file:

public <T> ReflectionDiffBuilder(final T lhs, final T rhs, final ToStringStyle style) {
    this.left = lhs;
    this.right = rhs;
    diffBuilder = new DiffBuilder(lhs, rhs, style);
} 

Here, the generic type is used to ensure that lhs and rhs are the same type.

Another example is from the spring-framework, in the RootBeanDefinition.java file:

public <T> RootBeanDefinition(@Nullable Class<T> beanClass, @Nullable Supplier<T> instanceSupplier) {
    super();
    setBeanClass(beanClass);
    setInstanceSupplier(instanceSupplier);
}

It’s a similar story: the beanClass type and the instanceSuppler type are locked together.

Pattern 2: Constrain the type of a parameter

Another use of a generic constructor is to constrain the type. A generic type can have multiple type constraints. For example, a generic type definition may be as complex as where T is the generic type, A can be a class or an interface type, and B and C must be interfaces (since Java does not have multiple class inheritance).

An awesome example of this comes from the vaadin framework, in the ActionManager.java file:

public <T extends Component & Container & VariableOwner> ActionManager(T viewer) {
    this.viewer = viewer;
}

The usefulness of this declaration is not immediately apparent, but take a look at how viewer is invoked throughout the class (where all comments were added by me):

    private void requestRepaint() {
        if (viewer != null) {
            // Calling method of the Component interface
            viewer.markAsDirty();
        }
    }

    public <T extends Component & Container & VariableOwner> void setViewer(
            T viewer) {
        // ...
        if (this.viewer != null) {
            // A safe cast to the Container interface 
            ((Container) this.viewer).removeActionHandler(this);
        }
        // ...
    }

    // ...

    public void paintActions(Object actionTarget, PaintTarget paintTarget)
            throws PaintException {

        // ...

        if (!actions.isEmpty() || clientHasActions) {
            actionMapper = new KeyMapper<>();

            // A safe cast to the VariableOwner interface
            paintTarget.addVariable((VariableOwner) viewer, "action", "");

            // ...
        }
        // ...
    }

We can see how each constraint is used, and we can be certain that the casts are safe because of the generic type constraint.

Guava also has a nice example of this in the Element.java file, but rather than casting at each invocation, the parameter is stored into two separate fields, one per type:

class Element extends AccessibleObject implements Member {

  private final AccessibleObject accessibleObject;
  private final Member member;

  <M extends AccessibleObject & Member> Element(M member) {
    checkNotNull(member);
    this.accessibleObject = member;
    this.member = member;
  }

  // ...

  @Override
  public final Annotation[] getAnnotations() {
    return accessibleObject.getAnnotations();
  }

  // ...

  @Override
  public final String getName() {
    return member.getName();
  }

    // ...
}

Sadly, Java only lets us declare these amazing type intersections with generics, and not on type declarations directly. But other languages allow you to specify intersection types like they were any other type. Specifically, Ceylon (another JVM language) allows you to define intersection types. In Ceylon, we might write the Element class as:

class Element(AccessibleObject&Member accessibleMember) 
        extends AccessibleObject() 
        satisfies Member {

    shared actual Annotation[] getAnnotations() 
            => accessibleMember.getAnnotations();

    shared actual String getName() 
            => accessibleMember.getName();
}

Not only can we declare accesibleMember as both an AccessibleObject and a Member without needing an intermediary generic type, there’s also no need to cast or store the reference twice! The compiler is smart enough to figure out the typing. It is truly a thing of beauty.

Pattern 1 & 2

Of course, ensuring that two constructor parameters have the same type and refining the generic type are orthogonal concepts, and so they can be used together. Indeed, there are even a few examples of this being used in the real world, in gradle, there’s a file called DefaultBuildOutcomeAssociation.java:

public <S extends A, T extends A> DefaultBuildOutcomeAssociation(S source, T target, Class<A> type) {
    this.source = source;
    this.target = target;
    this.type = type;
}

and also in the jflex project, file ItIterator.java:

public <TX extends Iterable<T>> ItIterator(TX iterable1, TX iterable2) {
    arr = new ArrayList<Iterator<T>>(2);
    arr.add(iterable1.iterator());
    arr.add(iterable2.iterator());
    arrIterator = arr.iterator();
}

Pattern 3: Anti-patterns

I found many uses of generic constructors where they weren’t actually needed.

For example, in the apache-opennlp project, the file AbstractSampleStreamFactory.java contains constructors such as:

public abstract class AbstractSampleStreamFactory<T> implements ObjectStreamFactory<T> {

  protected Class params;

  protected <P> AbstractSampleStreamFactory(Class<P> params) {
    this.params = params;
  }
  // ...
}

The <P> in this case is entirely unnecessary, and would have been more clearly written as

public abstract class AbstractSampleStreamFactory<T> implements ObjectStreamFactory<T> {

  protected Class<?> params;

  protected AbstractSampleStreamFactory(Class<?> params) {
    this.params = params;
  }
  // ...
}

Another example is found in the presto project, in the DaoSuppler class:

public class DaoSupplier<T>
{
    private final Class<? extends T> type;
    private final T dao;

    public <X extends T> DaoSupplier(IDBI dbi, Class<X> type)
    {
        requireNonNull(dbi, "dbi is null");
        requireNonNull(type, "type is null");
        this.type = type;
        this.dao = onDemandDao(dbi, type);
    }
    // ...
}

Where again, the type X can be replaced with a ?.

I have a sneaking suspicion that what we see today as “unnecessary” type qualifications, were actually necessary in earlier versions of Java, and the javac’s resolver has improved through the years, making these declarations unnecessary in modern Java code. Unfortunately, I haven’t been able to download an older version of the JDK to test this hypothesis.

The Final Tally

As mentioned before, there were many instances of generic constructors that weren’t actually necessary. Of the 116 constructors found, I counted 70 of them that were unnecessary. There were also a number of generic constructors that were specifically written to test some parser or some rule, such as pmd‘s ParserCornerCases.java, where we can see that the generic constructor was only written to test that generic constructors are handled correctly in their style checker. These kinds of generic constructors shouldn’t count in our final tally. I counted 22 such usages (where many were also examples of unnecessary usages).

So if we subtract all unnecessary usages and all test cases, here’s the final tally:

  • Total usages of generic constructors: 48
  • Total “force parameter types to match” (pattern 1): 24
  • Total “constrain the type of a parameter” (pattern 2): 27
  • Total pattern 1 & pattern 2: 3

Conclusion

So how rare are generic constructors? Why not compare with a similar feature? I also counted the number of methods with a generic definition, and discovered:

  • 40,967 methods with generic constructors in 9,992 unique files.

Even if the ratio of unnecessary usages and usages that ony exist for testing purposes was the same, it is clear that generic constructors are exceedingly rare. Even so, they can be a powerful tool for those rare occasions you do need it.

One thought on “Generic Constructors: What are They Good For?

  1. Thanks I was wondering what . Nice article.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s