I would like to present a scenario where it would be nice to:
- have a generic interface,
- have various generic base classes implement it,
- provide subclasses for the generic base classes,
- somehow get a non-generic interface to the subclasses.
Confused? Read on and tell me what you think about the solution.
Let’s say I want a service to provide any kind of object based on any kind of key
and from anywhere (DB, file etc).
For a good start let’s create a proper interface:
public interface ObjectProvider<K, V> {
V provide(K key);
}
In this way I will be able to provide any kind of implementation.
I want to provide objects from a database and a file.
Let’s assume that providing V based on K can be done with the same
DB logic regardless of object types. So I can write a generic base class
for DB access:
public class DBObjectProvider<K, V> implements ObjectProvider<K, V> {
public V provide(K key) {
V v = null;
//some common DB logic to get V based on K
return v;
}
}
Actually, getting objects from a file is also object type independent:
public class FileObjectProvider<K, V> implements ObjectProvider<K, V> {
public V provide(K key) {
V v = null;
//some common file reading logic to get V based on K
return v;
}
}
Ok, by now I have two generic classes that I may use to get anything I want.
Now, I want to use one of those generic implemenentations to get a String object based on a String key from a database. Additionally, I want to it defined as a bean using Spring XML.
I guess there is no way to define a generic bean in Spring XML (am I right?) so I
will create a proper class instead. All I need to do is:
public class DBStringStringProvider extends DBObjectProvider<String, String> {
}
Right now I can inject this bean into any:
private ObjectProvider<String, String> objectProvider;
All is fine, now the key part.
I could make use of my useful DB String-String provider in a lot of ways (hhmmm… ok not really). Let’s say I want to make a web service out of it.
Suppose I want to expose DBStringStringProvider as a CXF web service and test it.
The problem is that the client code for such a web service looks like this:
JaxWsProxyFactoryBean factory = new JaxWsProxyFactoryBean();
factory.setServiceClass(<INTERFACE>.class);
<INTERFACE> client = (<INTERFACE>) factory.create();
so I need a non-generic interface for DBStringStringProvider which I don’t have because it extends its generic base class.
I can do the following:
Extend the ObjectProvider<String, String> interface with another one:
@WebService
public interface StringStringProvider extends ObjectProvider<String, String> {
@WebMethod
String provide(String key);
}
Additionally implement it where it is sort of already implemented by the generic base class:
public class DBStringStringProvider extends DBObjectProvider<String, String>
implements StringStringProvider {
}
I’m feeling a little bit uncomforable about implementing the same interface where it is already introduced by the base class.
But in this way I’m able to make use of the WS client:
factory.setServiceClass(StringStringProvider.class);
StringStringProvider client = (StringStringProvider) factory.create();
My question is: Is it a good practice to do what I just did? If not, is there any other way?
This is just one scenario where it would be great to have a non-generic interface for something
that has been defined generic.
I am loathe to criticize the motives for the level of abstraction in the question posed. For the sake of consistency of interfaces and reduction of duplicated code, these scenarios arise from time to time. Java, and icky service frameworks in particular, make this task much more complex than it needs to be; such needless complexity does not mean the original design is bad. Every day Java makes me clench my teeth for any number of reasons pertaining to pointless verbosity.
I will happily confirm that defining a gratuitous concrete subinterface is acceptable and common practice. You are forced to take this action because you are working around a limitation in the service framework (parametrized interfaces not allowed).
The only thing to be concerned with is that you are really trying to do a typedef in Java. The best I have been able to do w.r.t. typedefs in Java results in an astoundingly ugly construction that would make the commenters on your original post, judging by their aversion to complexity, want to gauge out their own eyes.
I doubt that in practice you will encounter any problems, but in general psuedo-typedefs such as this example can cause unexpected pain. In the general case, when you create a trivial subinterface
Tof interfaceS, it is tempting to useTin place ofSwherever it applies.Smight be a long-winded parametrized Java class expression that’s a pain in the ass to read or type, for example, andTmight be nice and short. But for method/service call/constructor arguments, you still needSas the declared type or you risk limiting the usefulness of your method or class. A contrived example:There is no reason for
Serviceto only acceptInfo, asInfoadds no API toMap< String, List< FooBar > >; as written, callers of doSomething can’t take any old map as an input; they have to create a newInfosomehow and initialize it with the contents of that map, because Java (like nearly every other widely-used enterprise language) uses nominative typing.