views:

1370

answers:

3

I have a fairly complicated set of generic classes in Java. For example, I have an interface

interface Doable<X,Y> {
  X doIt(Y y);
}

and the implementation

class DoableImpl implements Doable<Foo<Bar<Baz,Qux>>,Foo<Bar<Zot,Qux>>> {
  Foo<Bar<Baz,Qux>> doIt(Foo<Bar<Zot,Qux>> fooBZQ) { ... }
}

In the real implementation, Doable has quite a few methods and so Foo<Bar<Baz,Qux>>, etc., appear over and over again. (Believe it or not, the generic types are quite a bit more painful than this. I've simplified them for the example.)

I'd like to simplify these, to save myself typing and to ease the strain on my eyes. What I'd like is to have a simple "type alias" for Foo<Bar<Baz,Qux>>, etc., say FooBBQ and FooBZQ.

My current idea is to define wrapper classes:

class FooBBQ { 
  public static FooBBQ valueOf(Foo<Bar<Baz,Qux>> fooBBQ) { 
    return new FooBBQ(fooBBQ); 
  }
  private Foo<Bar<Baz,Qux>> fooBBQ;
  private FooBBQ(Foo<Bar<Baz,Qux>> fooBBQ) { 
    this.fooBBQ = fooBBQ; 
  }
  public Foo<Bar<Baz,Qux>> toGeneric() {
    return fooBBQ;
  }
}

class FooBZQ { /* pretty much the same... */ }

class DoableImpl implements Doable<FooBBQ,FooBZQ> { 
  FooBBQ doIt(FooBZQ fooBZQ) { ... }
}

This works well, but it has a few drawbacks:

  1. We need to define separate wrappers for each generic instance. The wrapper classes are short and stylized, but I can't figure out a way to macro-ize them.
  2. We have the translation overhead (conceptually, if not operationally) of calling valueOf and toGeneric to convert between FooBBQ and Foo<Bar<Baz,Qux>>. For example, if doIt calls into some library routine that expects a Foo<Bar<Zot,Qux>> (which the real implementation does), we end up with something like

    return FooBBQ.valueOf( libraryCall( fooBZQ.toGeneric() ) )
    

    where we would originally have had

    return libraryCall(fooBZQ);
    

Is there some other way to get the "type alias" behavior I want here? Perhaps using some third-party macro toolset? Or do I need to accept that I'm going to have to do a lot of typing, one way (using the generic types in the implementation) or the other (writing wrappers for them)? Maybe having this many generic parameters flying around is just a bad idea and I need to re-think the problem?

[UPDATE] OK, I'm banning any further "don't do that" answers. Take it as a given that Foo<Bar<Baz,Qux>> has genuine value in my problem domain (Pete Kirkham may be right that it has enough value to get a proper wrapper class with a descriptive name). But this is a programming problem; don't try to define the problem away.

+1  A: 

Maybe having this many generic parameters flying around is just a bad idea and I need to re-think the problem?

Very probably. Do need to specialise 'Doit' in 8 dimensions?

In a lot of cases, these types don't exist in a vacuum and you should be thinking what domain objects your 'wrapper' represents rather than using them as a coding convenience.

Pete Kirkham
+1  A: 

I would say that, yes, you need to rethink the problem. The declaration

 class DoableImpl implements Doable<Foo<Bar<Baz,Qux>>,Foo<Bar<Zot,Qux>>> {
    Foo<Bar<Baz,Qux>> doIt(Foo<Bar<Zot,Qux>> fooBZQ) { ... } 
 }

is a pretty clear case of overusing generics.

JesperE
I disagree. It may not be overuse of generics ... it may be just an unclear use of generics. If it provides additional type safety, then it may be worth the pain, but there also must be a way to do this more clearly.
Eddie
Would you believe me if I said it's actually fairly clear (just extremely verbose) in the real code? Obviously there's no point in parameterizing your objects by Foo, Bar, and Qux...
Chris Conway
I have seen (and done) worse, there are times when it makes sense... but they can be hard to follow. However the typesafety can be worth it.
TofuBeer
I'm toying with algebra (Rings, Fields, various ways to construct them) in Java and I'm getting close to this level of generics. So it might be really needed. But one should try hard to keep it as simple as possible.
starblue
+3  A: 

If you want full type safety, I don't think you can do better without some kind of wrapper classes. But, why not make those classes inherit/implement the original generic versions, like this:

public class FooBBQ extends Foo<Bar<Baz,Qux>> {
...
}

This eliminates the need for toGeneric() method, and it is more clear, in my opinion, that it is just a type alias. Also, generic type can be cast into FooBBQ without a compiler warning. It would be my personal preference to make Foo, Bar, Baz... interfaces, if possible, even if some code duplication would occur in implementation.

Now, without knowing concrete problem domain, it is hard to say whether you need, say FooBBQ, like in your example, or perhaps a:

public class FooBar<X, Y> extends Foo<Bar<X, Y>> {
...
}

On the other hand, have you thought about simply configuring Java compiler not to show some of the generic warnings, and simply omit the parts of generic definition? Or, use strategically placed @SuppressWarnings("unchecked")? In other words, can you make DoableImpl only "partly genericized":

class DoableImpl implements Doable<Foo<Bar>>,Foo<Bar>> {
    Foo<Bar> doIt(Foo<Bar> foobar) { ... } 
}

and ignore the warnings for the sake of less code clutter? Again, hard to decide without a concrete example, but it is yet another thing you can try.

javashlook
Extending Foo<Bar<Baz,Qux>> gets you one-way translation free (i.e., implicit up-casting) at the cost of having to write more complicated constructors (for explicit down-casting). It's not clear to me that that's a win. I'd like to avoid @SuppressWarnings annotations as much as possible.
Chris Conway