在设计并行类层次结构时遇到了一些编译器错误。问题似乎源于Java的局限性(与C ++不同),没有公共“typedef”允许从泛型类外部访问类型参数。我想以下示例代码最能说明我的问题。
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> { }
通过以上设置,编译器会抱怨以下代码:
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
}
}
}
错误消息显示为:
BeanCounter类型中的方法addBean(捕获#2-of?extends Bean)不适用于参数(Bean)
这是可以理解但很尴尬,因为src和des显然属于同一类型。然而,我们无法移动他们的bean,因为我们只能使用基类型“Bean”。在C ++世界中,我们可以使用“C :: BeanType”来引用具体的bean类型。为了解决这个问题,我决定将merge逻辑放入DefaultBeanCounter中,其中bean类型是已知的:
所以我添加了以下方法:
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);
}
}
现在BeanCounterMaster看起来像:
public class BeanCounterMaster <C extends BeanCounter<? extends Bean>> {
public void mergeBeans(C des, C src) {
des.mergeBeans(src); // Still got compiler error
}
}
新的编译器错误如下:
BeanCounter类型中的方法mergeBeans(BeanCounter)不适用于参数(C)
我在这里缺少什么?
答案 0 :(得分:2)
仅使用C extends BeanCounter<? extends Bean>
时,您将丢失有关特定bean类型的信息。因此,当在src.getBeans()
上进行迭代时,您只知道自己正在使用Bean
,并且在将它们添加到{{1}时,编译器无法确定这些应该是“相同类型”。 }。
解决方法是给des
两个类型参数:
BeanCountMaster
请注意,如果您愿意,public class BeanCounterMaster <C extends BeanCounter<B>, B extends Bean> {
public void mergeBeans(C des, C src) {
for (B bean : src.getBeans()) {
des.addBean(bean);
}
}
}
可能只是静态generic method:
mergeBeans
在此,我删除了public static <B extends Bean> mergeBeans(
BeanCounter<B> des,
BeanCounter<? extends B> src
) {
for (B bean : src.getBeans()) {
des.addBean(bean);
}
}
,转而只使用C
BeanCounter
,以防您想要考虑。{1}}请注意,我还B
一个src
,这意味着您可以复制,例如将BeanCounter<? extends B>
改为BeanCounter<FrozenGreenBean>
(请参阅What is PECS (Producer Extends Consumer Super)?)。