Patterns, Java Generics, and locking together N parameterised types


Java generics are for patterns – meta-classes. The most obvious example is the collections interfaces. In some patterns it’s necessary to lock together mutually dependent classes that do not have in inheritance hierarchy. This can be done in Java, no worries. As with all java generics: the declarations are hideous, but once you do them the classes you derive look great and the code writes itself. Literally, if your IDE does auto-completion.

Over at agency X, we had a database in which we wanted to journal pretty much every table. As a result, we produced a whole stack of classes in pairs: Person/PersonVersion, Address/AddressVersion, (umm…) Breakfast/BreakfastVersion and so on. This is a pattern – it’s a common thingy that is meta beyond inheritance. We want to achieve a couple of things here. There should be a pair of common superclasses Versionable and Version, containing useful functionality. If I have a Versionable object, I want to be able to ask it “what is your latest version”, or “what version of you was current at timestamp“? If I have a Version object, I want to be able to ask “What Versionable are you a version of”? And I want these things to be typesafe.

Now, this seems pretty straightforward, so let’s start coding:

class Versionable<VERSION extends …

Ah. A snag. Person needs to be the Versionable of PersonVersion, and Address the Versionable of AddressVersion. We would want to say something like:

class Person extends Versionable<PersonVersion> { …; }
class PersonVersion extends Version<Person> { …; }
class Address extends Versionable<AddressVersion> { …; }
class AddressVersion extends Version<Address> { …; }

But how to we do the declaration of the generic type? Well, to fit the code above, we could go:

class Versionable<T extends Version>  { …; }
class Version<T extends Versionable>  { …; }

But that won’t do, because there are implied arguments there:

class Versionable<T extends Version<?>>  { …; }
class Version<T extends Versionable<?>>  { …; }

Those question-marks tell us what we are missing. We need to declare not only that Versionables are always associated with Versions, but that the Versions they are associated with are Versions of the Versionable you are declaring.

If that makes sense.

To fix this, the generic declaration needs to include both types: each type refers to its partner and also to itself, using that class Foo<T extends Foo<T>> reflexive back-hook construct. It’s weird, but it can only ever be set to “the class where this parameter gets bound”. You can leave it unbound, but if tyou bind it then it must be “self”.

abstract class Versionable<
  T extends Versionable<T, V>,
  V extends Version<T, V>> {
 protected Map<Date, V> versions = null;
 public V getCurrent() { … }
 public V getCurrent(Date timestamp) { … }
 protected abstract V newVersion();
}

abstract class Version<
  T extends Versionable<T, V>,
  V extends Version<T, V>> {
 protected T base;
 public Version(T base) {this.base=base;}
 public T getBase() {return base;}
 public abstract boolean isPrettyMuchSameAs(V compareTo);
}

Each of them is declared as depending on itself and the other, simultaneously. I like to keep the sequence of parameters and their names identical – it reduces confusion. The point of the exercise is that you can now declare mutually interdependent classes:

class Breakfast extends Versionable<Breakfast, BreakfastVersion> { … }
class BreakfastVersion extends Version<Breakfast, BreakfastVersion> { … }

and so on. This means that BreakfastVersion.getBase() doesn’t simply return an unknown Versionable, or some Versionable known to have Versions that are of type BreakfastVersion, but it specifically returns a Breakfast object, and vice-versa. Thus, you can ask:

BreakfastVersion foo;
if(foo.isPrettyMuchSameAs(foo.getBase().getCurrent()) {…}

And have it all done in a type-safe way. It’s pretty obvious how this extends to N mutually interdependent classes. At our current site, we even have some funky stuff where something like this is subclassed fairly deeply, and one branch terminates before the other. This can be done, too. Let’s say you have two quite different types of breakfasts: Meat and Veg, but their Version objects are the same and you’d like to use the same class implementation for both.

abstract class Breakfast<T extends Breakfast<T>>
  extends Versionable<T, BreakfastVersion<T>> {
 public BreakfastVersion<T> newVersion() {
  return new BreakfastVersion<T>((T)this);
 }
}

class BreakfastVersion<T extends Breakfast<T>>
  extends Version<T, BreakfastVersion<T>> {
 public BreakfastVersion(T breakfast) { super(breakfast); }
 public boolean isPrettyMuchSameAs(
  BreakfastVersion<T> compareTo) { … }
}

class BreakfastMeat
 extends Breakfast<BreakfastMeat> { … }

class BreakfastVeg
 extends Breakfast<BreakfastVeg> { … }

Note how BreakfastMeat and BreakfastVeg are unparameterised classes, but the the generic class that serves as a version for each is parameterised with the kind of breakfast that it is a version of. We dont bother with subclasses for it, which means that it is always paramterised wherever it appears – but that’s ok. The whole thing is typesafe, and adding new kinds of breakfasts sharing the generic version class is straightforward. Heck – you can even subclass the version class if you want.

Advertisements

3 Responses to Patterns, Java Generics, and locking together N parameterised types

  1. Robyn Lawrence says:

    Can’t say I really understand this, but my java lecture yesterday briefly talked about generics and totally confused everyone – the lecturer included.

    I like your examples and the meat & veg breakfast options.

  2. bob says:

    It should just be declared as

    abstract class Versionable< T, V extends Version>

    and

    abstract class Version< T extends Versionable, V >

    and

    abstract class Breakfast

    The extra bounds are useless

    • Paul Murray says:

      It is not the case that the extra bounds are useless. A Version object has a mehod “get me the versionable that I am part of”, and Versionable has a method “get the latest version”. Without the extra bounds, the object returned by these methods are unknown subclasses of Version. With those bounds, it becomes possible to properly write a method Version.compareMeToTheLatestVersion(), because Version.getMyVersionable().getLatestVersion() is properly typed. That’s the point.

      The declaration you have – T extends Versionable – will give you a “use of a raw type” warning.

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 )

Google+ photo

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

Connecting to %s

%d bloggers like this: