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

1 comment to pitfall on implementing generic methods

Leave a Reply

 

 

 

You can use these HTML tags

<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>