One of the cases I’ve had to deal with in gwt-pectin is creating Reduce style functions that operate on a collection of values. The basic idea is to define interface of the form.
public interface Reduce<T,S> {
T reduce(List<S> source);
}
I use this on my ReducingValueModel<T,S> to automatically compute fields such as a sum’s of numbers, or a string representation of a list and so forth. So on my ReducingValueModel I have a method to configure the reducing function to use as follows.
public class ReducingValueModel<T,S> {
public void setFunction(Reduce<T,S> function) {
...
}
}
This works fine when define a new function each different combination of T and S. The trouble appears when you want to create a generic Reduce function that you can use on any ReductingValueModel. For example:
public class DefaultListToStringFunction<String, Object> {
public String reduce(List<Object> values) {
// concate our list values and return the result.
...
return result;
}
}
So now lets try and use it.
ReducingValueModel<String, Integer> reducingModel = ...; // This barfs!!! reductingModel.setFunction(new DefaultListToStringFunction());
And of course it’s obvious why since our DefaultListToStringFunction is a Reduce<String, Object> and not Reduce<String, Integer>. So we bung in the standard <? super S> clause on the ReducingValueModel so it can accept a function that works on any super type.
public class ReducingValueModel<T,S> {
private Reduce<T, ? super S> function;
public void setFunction(Reduce<T,? super S> function) {
this.function = function;
recompute();
}
}
And while it looks like this should work, it doesn’t. The problem is that were I’m using the Reduce function it’s now defined as a Reduce<T, ? super S> so it can’t accept a List<T> as an argument.
public class ReducingValueModel<T,S> {
private Reduce<T, ? super S> function;
protected void recompute() {
ArrayList<S> values = ...; // prepare the values..
// now this won't compile now
computedValue = function.compute(values);
fireValueChangeEvent(computedValue);
}
}
Fortunately the fix is simple and somewhat obvious when you think about it. We need to update our Reduce<T,S> interface to accept any values that extend S. I.e.
public interface Reduce<T,S> {
T reduce(List<? extends S> source);
}
Now ReducingValueModel can accept functions that works on any super type of S and our DefaultListToStringFunction can accept any list whose values extend Object.
So the two things to do are:
- Allow users to configure functions that use <? super S> for the source values.
- Make sure your functions can operate on source collections of type <? extends S>.
All good.


