并行类层次结构上下文中的Java泛型问题

时间:2013-12-13 04:39:49

标签: java generics

在设计并行类层次结构时遇到了一些编译器错误。问题似乎源于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)

我在这里缺少什么?

1 个答案:

答案 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)?)。