I've currently got the following classes/interfaces laid out. The type T
represents the format of data returned from DataProvider
implementations. I'm using a factory so I don't need to attach type information to MyStreamingOutput
. I'm using HK2
to inject the DataProviderFactory
into MyStreamingOutput
.
public interface DataProvider<T> {
public T next() { ... }
...
}
public final class SQLDataProvider<T> {
public SQLDataProvider(final String query, final RowMapper<T> rowMapper) { ... }
}
public interface DataProviderFactory {
public <T> DataProvider<T> getDataProvider(final String query, final RowMapper<T> rowMapper);
...
}
public final class SQLDataProviderFactory {
public <T> DataProvider<T> getDataProvider(final String query, final RowMapper<T> rowMapper) {
return new SQLDataProvider<T>(query, rowMapper);
}
}
public final class MyStreamingOutput implements StreamingOutput {
public MyStreamingOutput(final DataProviderFactory dpFactory) { ... }
@Override public void write(final OutputStream outputStream) throws IOException { ... }
}
This all works fine. Now I'm trying to set up a unit test for MyStreamingOutput
, but I'm running into a couple of roadblocks. I wrote the following additional class for testing purposes:
public final class DataProviderFactoryStub implements DataProviderFactory {
private final DataProvider dataProvider;
public DataProviderFactoryStub() {
this.dataProvider = new DataProviderStub();
}
public DataProviderFactoryStub(final DataProvider dataProvider) {
this.dataProvider = dataProvider;
}
@Override
public <T> DataProvider<T> getDataProvider(final String query, final RowMapper<T> rowMapper) {
return this.dataProvider;
}
}
The binding occurs in
final class QueryTestResourceConfig extends ResourceConfig {
public QueryTestResourceConfig() {
...
this.register(new AbstractBinder() {
@Override
protected void configure() {
bind(DataProviderFactoryStub.class).to(DataProviderFactory.class);
}
});
}
}
I can successfully inject this class into MyStreamingOutput
, but it has a compiler warning because the typing information used by getDataProvider()
isn't shared by the instance passed into the factory. I can't add type information to the DataProviderFactoryStub
class because then it no longer implements the DataProviderFactory
interface. I don't want type information on the interface because it's wrong - outside of the Stub case, the factories shouldn't care about the type returned by DataProvider
instances. I'd very much like to avoid using setters for the query
and rowMapper
parameters because I consider it bad design in this case.
I can't shake the feeling that I'm either missing something subtle in my application of generics or something obvious in my application of dependency injection. What is the right way to address this use case? It seems like this is the kind of problem DI is meant to address, but I can't see how to fix it.
Unfortunately, due to type erasure, it isn't possible to do what I want. I will have to look at refactoring the existing code.