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.
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
SohoTablegetSohoTableOfContact();
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;
Hi, Gene
ReplyDeleteThis example is great. I am trying to implement the same type of functionality. Can you please share the entire code ?
Regards, DJ
Yes nice article. COde would be helpful to understand fully.
ReplyDelete