Java Generics – Balancing Expressiveness and Simplicity

extensibilitygenericsjavareadability

I'm developing some code that utilizes generics, and one of my guiding principles was to make it usable for future scenarios, and not just today's. However, several coworkers have expressed that I may have traded off readability for the sake of extensibility. I wanted to gather some feedback about possible ways to resolve this.

To be specific, here is an interface that defines a transform – you start with a source collection of items, and you apply the transform to each element, storing the results in a destination collection. Additionally, I want to be able to return the destination collection to the caller, and rather than forcing them to use a Collection reference, I want them to be able to use whatever collection type they actually provided for the destination collection.

Finally, I make it possible for the type of items in the destination collection to be different from the type of items in the source collection, because maybe that's what the transform does. In my code, for instance, several source items make up one destination item after the transform.

This yields the following interface:

interface Transform<Src, Dst> {
    <DstColl extends Collection<? super Dst>> DstColl transform(
                Collection<? extends Src> sourceCollection,
                DstColl                   destinationCollection);
}

I tried to be all nice and apply Josh Bloch's PECS principle (producer extends, consumer super) to make sure the interface is usable with super- and sub-types where appropriate. The end result is somewhat of a monstrosity.

Now, it would have been nice if I could extend this interface and specialize it somehow. For example, if I don't really care about playing nice with subtypes of the source items and supertypes of the destination items, I could have:

interface SimpleTransform<Src, Dst> {
    <DstColl extends Collection<Dst>> DstColl transform(
                  Collection<Src> sourceCollection,
                  DstColl         destinationCollection);
}

But there is no way to do that in Java. I want to make implementations of this interface something that others would actually consider doing, as opposed to running in fear. I've considered several options:

  • Don't return the destination collection. Seems weird given that you do a transform but get nothing back.
  • Have an abstract class that implements this interface, but then translates the parameters to something easier to use and calls another method "translateImpl()" which has the simpler signature and thus presents less of a cognitive burden for implementers. But then it's weird to have to write an abstract class just to make an interface user-friendly.
  • Forgo extensibility and just have the simpler interface. Possibly couple that with not returning the destination collection. But then that limits my options in the future.

What do you think? Am I missing an approach I could use?

Best Answer

Guava Collections already provides this functionality and if you can hold off a little longer, Java 8 will provide this also :-). FWIW I think your use of ? extends T is the correct idiom, not much you can do given that Java uses Type Erasure

Related Topic