Monday, January 24, 2011

Using Gin-Guice With Java Generics (Cell Table/DAO Example)

Last year was so interesting and I have discovered so many interesting methods, ideas, patterns, and frameworks for developing professional software.  A big proportion of this learning curve has been embracing Test-Driven Development and the necessary changes required to the structure of your code.  One design pattern is the Dependency-Injection (DI) pattern where an objects dependencies are injected into the constructor.  This allows us to clearly see what the dependencies of an object are, and using a framework allows us to inject our implemented classes.  Spring offers a DI framework, which I hope to experiment at some point, but for now, and because many of the Google GWT examples use it, I am using Google GIN on the client side GWT projects and Google GUICE on the server.

Explaining Generics is a little more complicated and I defer to someone more experienced.  So a quick web search yields this:
"Generics provides a way for you to communicate the type of a collection to the compiler, so that it can be checked. Once the compiler knows the element type of the collection, the compiler can check that you have used the collection consistently and can insert the correct casts on values being taken out of the collection."
Now here comes the tricky part.  How do we inject a generic type class using Gin/Guice?

I always like to lead by example so lets talk about the example used here.  Imagine you have a base class for all of your tables you are gong to render.  However depending on the annotation used where you inject the base table, then that dictates what the type of the generic class used.  In addition the annotation decides what ColumnFormatter class is used to render table.  So in summary:
  • 1 Base Class SohoTable<T>
  • Several ColumnFormatter classes depending on table used.
Declaration of SohoTable
public class SohoTable<T extends HasReadId> extends Composite implements
  HasEventBus, HasTableChangedEventHandler {
@Inject
public SohoTable(ColumnFormatter columnFormatter) {

Here the base class shows the generics declaration.  Here you can replace HasReadId with any interface your common DAOs implement.  For simplicity we just ensure that HasReadId has a method to get the unique Id of the row.

The constructor requires a ColumnFormatter implementation for the DAO object you are creating.   This is a little abstract, so hold off your questions until later.

ContactColumnFormatter
let us dig deeper into a real implementation.  So we have a table of Contacts which we are going to use the generic base class for therefore we need to create this class and implement the ColumnFormatter.
public class ContactColumnFormatter extends ColumnFormatter {

Create the annotation
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.FIELD, ElementType.PARAMETER })
@BindingAnnotation
public @interface ContactTableAnnot {

}

Ginjector
SohoTable getSohoTableOfContact();

Create the ContactTableProvider
public class ContactTableProvider implements Provider<SohoTable<Contact>>{

 private final Provider<ContactColumnFormatter> providerFormatter;

 @Inject
 public ContactTableProvider(Provider<ContactColumnFormatter> providerFormatter) {
  this.providerFormatter = providerFormatter;
 }

 @Override
 public SohoTable<Contact> get() {
  return new SohoTable<Contact>(providerFormatter.get());
 }

}
Here we inject the correct ColumnFormatter for the Contact table.

Wire up the classes in the ClientModule (AbstractGinModule)
bind(ContactColumnFormatter.class);
bind(ContactTableProvider.class);
bind(new TypeLiteral<SohoTable<? extends HasReadId>>(){}).annotatedWith(ContactTableAnnot.class).toProvider(ContactTableProvider.class);
This is where we where the main action takes place. All the subclasses are wired up. The final line is the cool one. We need to use a TypeLiteral in order to wire up the generic elements. The annotated item shows how to wire up the annotation with the correct provider.

This is all very good but how do I use it?
@UiField(provided = true)
SohoTable<Contact> contactTable;

@Inject
public RolodexView(@ContactTableAnnot SohoTable<? extends HasReadId> contactTable) {
  this.contactTable = (SohoTable<Contact>) contactTable;

2 comments:

  1. Hi, Gene

    This example is great. I am trying to implement the same type of functionality. Can you please share the entire code ?

    Regards, DJ

    ReplyDelete
  2. Yes nice article. COde would be helpful to understand fully.

    ReplyDelete