I ran into some compiler errors when designing parallel class hierarchies. The issue seems stem from the limitation of Java where (unlike C++) there's no public "typedef" to allow access to type parameter from outside of a generic class. I guess following sample code best illustrates my question.
public interface Bean { }
public class GreenBean implements Bean { }
public class RedBean implements Bean { }
// All BreanCounter classes implement this interface and interact with their
// corresponding concrete bean classes.
public interface BeanCounter <B extends Bean> {
public void addBean(B bean);
public Collection<B> getBeans();
}
// DefaultBeanCounter implements logic shared by GreenBeanCounter and RedBeanCounter
public class DefaultBeanCounter <B extends Bean> implements BeanCounter<B> {
private List<B> beans = new ArrayList<B>();
@Override
public void addBean(B bean) { beans.add(bean); }
@Override
public Collection<B> getBeans() { return beans; }
}
public class GreenBeanCounter extends DefaultBeanCounter<GreenBean> { }
public class RedBeanCounter extends DefaultBeanCounter<RedBean> { }
With above setup, compiler complains about the code below:
public class BeanCounterMaster <C extends BeanCounter<? extends Bean>> {
public void mergeBeans(C des, C src) {
for (Bean bean : src.getBeans()) {
des.addBean(bean); //compiler error on this line
}
}
}
The error message reads:
The method addBean(capture#2-of ? extends Bean) in the type BeanCounter is not applicable for the arguments (Bean)
This is understandable but awkward, as src and des are obviously of the same type. And yet, we can't move their beans around because we can only use base type "Bean". In C++ world, we would be able to use "C::BeanType" to refer to the concrete bean type. To work around this, I decide to put merge logic into DefaultBeanCounter where bean type is known:
So I added following methods:
public interface BeanCounter <B extends Bean> {
public void mergeBeans(BeanCounter<B> anotherCounter);
}
public class DefaultBeanCounter <B extends Bean> implements BeanCounter<B> {
@Override
public void mergeBeans(BeanCounter<B> anotherCounter) {
for (B bean : anotherCounter.getBeans())
addBean(bean);
}
}
Now BeanCounterMaster looks like:
public class BeanCounterMaster <C extends BeanCounter<? extends Bean>> {
public void mergeBeans(C des, C src) {
des.mergeBeans(src); // Still got compiler error
}
}
The new compiler error reads:
The method mergeBeans(BeanCounter) in the type BeanCounter is not applicable for the arguments (C)
What am I missing here?