Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Type aliases for Java generics

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.

like image 690
Chris Conway Avatar asked Mar 25 '09 21:03

Chris Conway


People also ask

Does Java have type aliases?

In Java, Alias is used when reference, which is of more than one, is linked to the same object. The issue with aliasing is when a user writes to a particular object, and the owner for the several other references do not expect that object to change.

What are type aliases?

Type aliases Type aliases provide alternative names for existing types. If the type name is too long you can introduce a different shorter name and use the new one instead. It's useful to shorten long generic types. For instance, it's often tempting to shrink collection types: typealias NodeSet = Set<Network.

What is type as generic in Java?

Generic types and methods are the defining new feature of Java 5.0. A generic type is defined using one or more type variables and has one or more methods that use a type variable as a placeholder for an argument or return type. For example, the type java. util.

Which types can be used as arguments of generics?

The actual type arguments of a generic type are. reference types, wildcards, or. parameterized types (i.e. instantiations of other generic types).


2 Answers

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.

like image 61
javashlook Avatar answered Sep 30 '22 22:09

javashlook


Scala has nice support for type aliases. For example:

type FooBBQ = Foo[Bar[Baz,Qux]] 

I realize that this answer won't be helpful if you don't have the option of switching to Scala. But if you do have the option of switching you might have an easier time.

like image 30
Jesse Hallett Avatar answered Sep 30 '22 21:09

Jesse Hallett