pitfall on implementing generic methods
disclaimer: don´t get me wrong. 90% of the time i like generics. most of the time it only starts getting weird, when you end up using something like
| Java | | copy | | ? |
IModel< List<EntityModel<Address<Person>>>> getAddressModels(); |
but hey, nothing is perfect.
recently stumbled over an easy to avoid mistake you can make introducing generics to your APIs. this tale is about Wicket, which has a bunch of nice classes, that give you the opportunity to load a text file from somewhere and replace some variables in there:
| Java | | copy | | ? |
/** |
* Represents a text template that can do variable interpolation. |
*/ |
public abstract class TextTemplate extends AbstractStringResourceStream |
{ |
... |
public String asString(Map< String, Object> variables) |
{ |
if (variables != null) |
{ |
return new MapVariableInterpolator(getString(), variables).toString(); |
} |
return getString(); |
} |
... |
} |
as you know this is about generics it is easy to spot the error isn´t it ?
the problem here is that it is hard to use because you need a Map< String, Object>.
while we all know that it would be hard to provide something that does not inherit from Object, the signature does not allow
< ? extends Object>(which is probably the same than < ?>) but needs the Object-type explicitly.
this makes it barely usable because
| Java | | copy | | ? |
PackagedTextTemplate p = new PackagedTextTemplate(Foo.class, "somefile.txt"); |
Map< String, String> map = new HashMap< String, String>(); |
map.put("foo","bar"); |
p.asString(map); |
will be answered by
the method asString(Map< String,Object>) in the type TextTemplate is not applicable for the arguments (Map< String,String>)
you might argue that you can just parametrize the Map accordingly, but what if you grab it from somewhere?
and you cannot even cast to Map< String,Object>.
so all you can do is to bypass generics with:
| Java | | copy | | ? |
p.asString((Map) map); |
which is probably not what you want the users of your API to do
the interesting bit is, that MapVariableInterpolator does it the right way:
| Java | | copy | | ? |
public class MapVariableInterpolator extends VariableInterpolator |
{ |
public MapVariableInterpolator(final String string, final Map< ?, ?> variables) |
{ |
... |
} |
Conclusion
be sure to allow subclassed parameter-types in your APIs – “extends” really is your friend when you generify your APIs.
PS: i´m feeling bad to complain right after release of wicket 1.4 and not before ![]()
PPS: Issue WICKET-2409


FYI, the issue was posted and discussed on the ML there : http://www.nabble.com/Issue-WICKET-2409-td24781254.html
++