抽象递归泛型导致绑定不匹配

时间:2014-03-16 00:07:43

标签: java generics abstract-class

在尝试以逻辑方式构建我的类时,我发现了Java执行递归泛型的能力。这几乎就是我在结构方面寻找的东西,但我遇到了抽象类的问题。我认为Foo和Bar在这个例子中会非常混乱,所以我将我的课程命名为与我的实际项目有关。

public abstract class GeneCarrier<T extends GeneCarrier<T>> {
    protected Gene<T> gene;
    //...
}

public class Gene<T extends GeneCarrier<T>> {
    //...
}

public abstract class Organism extends GeneCarrier<Organism>{
    //...
}

public class Human extends Organism {
    public void foo(){
        Gene<Human> g; // Bound mismatch: The type Human is not a valid substitute for the bounded parameter <T extends GeneCarrier<T>> of the type Gene<T>
    }
}

我认为问题可能出在我的抽象Organism类的定义上,但这也产生了类似的错误:

public abstract class Organism extends GeneCarrier<? extends Organism>{
    //...
}

尝试使用递归模板定义的抽象类是否存在固有问题,或者我在类定义中犯了错误?

4 个答案:

答案 0 :(得分:3)

  

尝试使用递归模板定义的抽象类是否存在固有问题,或者我在类定义中犯了错误?

看起来你犯了一个错误。 Gene的类型参数T上的递归绑定需要Human的类型参数表示HumanGeneCarrier<Human>。但事实并非如此 - HumanGeneCarrier<Organism>

为了正确实现这个模式,递归类型参数应该沿着继承树向下传播,直到达到我想称之为“叶子”的类,在这种情况下似乎是Human

public abstract class Organism<T extends Organism<T>> extends GeneCarrier<T> {
    //...
}

public final class Human extends Organism<Human> {
    public void foo(){
        Gene<Human> g; // valid
    }
}

这解决了手头的问题,但你应该知道在Java中使用“自我类型”的起伏(通常称为Curiously Recurring Template Pattern)。我详细介绍了在这篇文章中实现这种模式及其缺陷:Is there a way to refer to the current type with a type variable?

一般来说,我发现开发人员尝试使用“自我类型”来在某些类上实现类型安全的“复制”方法(这似乎就是这种情况,因为你的类型名称与基因有关)。当发生这种情况时,我总是建议尝试将复制责任分离为单独的类型,以避免递归泛型的复杂性增加。 My answer here就是一个例子。

答案 1 :(得分:0)

通过继承层次结构,Human扩展了GeneCarrier<Organism>,但Gene期望<T extends GeneCarrier<T>>。因此,类型变量T应绑定到Human,但由于Human不会扩展GeneCarrier<Human>,因此不能这样做。

答案 2 :(得分:0)

此处的问题是Human扩展OrganismOrganismGeneCarrier<Organism>。但根据规范,Gene的类型参数必须是GeneCarrier,其“自身”作为类型参数(而不是任意超类型)。

一个解决方案允许超类型:在这种情况下,允许使用“Human extends Organism extends GeneCarrier”:

abstract class GeneCarrier<T extends GeneCarrier<? super T>> {
    protected Gene<T> gene;
}

class Gene<T extends GeneCarrier<? super T>> {}

abstract class Organism extends GeneCarrier<Organism>{}

class Human extends Organism {}

但据推测,这不是你想要的。

另一种方法是让Human 扩展Organism,但GeneCarrier<Human> - 在这种情况下,要求“将自己作为类型参数“会实现:

abstract class GeneCarrier<T extends GeneCarrier<T>> {
    protected Gene<T> gene;
}

class Gene<T extends GeneCarrier<T>> {}

abstract class Organism extends GeneCarrier<Organism>{}

class Human extends GeneCarrier<Human> {}

如果这不适用于您的情况,您可能希望更清楚地解释这些要求。其他替代方案也许是可能的,例如将类型参数作为Organism通过Organsim<T extends Organism<T>>类传递,但是......你会看到递归泛型往往会变得混乱和违反直觉,除了一些简单的情况。 / p>

答案 3 :(得分:0)

目前尚不清楚你要做什么。

首先,带有class GeneCarrier<T extends GeneCarrier<T>>等边界的类没用。您应该几乎总是使用class GeneCarrier<T>

您的实际错误源于Human扩展Organism,扩展GeneCarrier<Organism>,因此Human无法扩展GeneCarrier<Human>和{{ 1}}需要绑定GeneT。目前还不清楚为什么班级extend GeneCarrier<T>完全有这个要求。您没有显示该类的任何代码,因此似乎没有任何原因。

您需要什么样的约束取决于您对类型Gene执行的操作。例如,如果您所做的只是将T类型传递给T类型为GeneCarrier<T>的方法,那么T的范围就足够了。并且使用此绑定T extends GeneCarrier<? super T>可以正常工作。或者,即使是Gene<Human>的界限也足够了;没有任何代码就很难说清楚。

如果确实需要<T>(不太可能),则T extends GeneCarrier<T>必须延长Human。在这种情况下,GeneCarrier<Human>无法按其定义。如果你采用Paul Bellora的建议,Organism也参数化,那么它看起来像这样:

Organism