具有泛型和协变返回的Java并行继承层次结构

时间:2013-08-27 18:25:17

标签: java generics inheritance

假设我有两个继承层次结构:

class EntityA { ... }
class EntityB extends EntityA { ... }

class EntityAPrime extends AbstractPrime<EntityA> { ... }
class EntityBPrime extends EntityAPrime { ... }

我也喜欢协变返回类型:

public class EntityA {
  public EntityAPrime prime() {
    return new EntityAPrime();
  }
}

public class EntityB extends EntityA {
  @Override
  public EntityBPrime prime() {
    return new EntityBPrime();
  }
}

到目前为止一切顺利。

问题是我希望EntityBPrime最终扩展AbstractPrime<EntityB>,但由于它延伸EntityAPrime,它最终会扩展AbstractPrime<EntityA>

我可以使EntityAPrimeEntityBPrime通用,但后来我失去了协变返回类型:

public class EntityA {
  public EntityAPrime<EntityA> prime() {
    return new EntityAPrime();
  }
}

public class EntityB extends EntityA {
  @Override
  public EntityBPrime<B> prime() {  // ERROR: EntityBPrime extends EntityAPrime
    return new EntityBPrime();      // but can't substitute <EntityB> for <EntityA>
  }
}

如果我返回绑定的通配符但是disadvantages与之关联,那么一切都有效,我将无法在AbstractPrime上调用特定的setter。

我的另一个想法是让EntityAEntityB成为通用的。

public class EntityA<T extends EntityA> {
  public EntityAPrime<T> prime() { ... }
}

public class EntityB<T extends EntityB> extends EntityA<T> {
    @Override
    public EntityBPrime<T> prime() { ... }
}

这应该可行,但我认为它会变得混乱(我们有100多个实体会使用类似的模式)。

那么,有没有办法:

  • 保持协变回报
  • 不在EntityAEntityB
  • 上使用泛型
  • 没有prime()返回绑定的通配符
  • EntityBPrime最终延长AbstractPrime<EntityB>而不是AbstractPrime<EntityA>

注意:素数类是生成代码,但我可以控制生成代码。

2 个答案:

答案 0 :(得分:1)

首先,您的接口和类都被称为AB,这有点令人困惑。所以,我已经将这些类重命名为AImplBImpl。此外,您的课程APrimeBPrime未输入(他们需要),因此我假设他们分别在<T extends A><T extends B>上输入(如果我'我误解了你的意图/目标/要求,我道歉。

private interface A {}
private interface B extends A {}
private abstract class AbstractPrime<T extends A>{}
private class APrime<T extends A> extends AbstractPrime<T>{}
private class BPrime<T extends B> extends APrime<T>{}
private class AImpl {...}
private class BImpl {...}

正如您所考虑的,我的第一直觉是在接口上键入实现。这可行:

private class AImpl<T extends A> {
  public APrime<T> prime(){
    return new APrime<>();
  }
}

private class BImpl<T extends B> extends AImpl<T> {
  public BPrime<T> prime(){
    return new BPrime<>();
  }
}

但是,如果您在APrime<T extends A>BPrime<T extends B>键入了prime(),是否需要输入class APrime<T extends A> extends AbstractPrime<T>{} class BPrime<T extends B> extends APrime<T>{} public class AImpl { public APrime prime(){ return new APrime<>(); } } public class BImpl extends AImpl { public BPrime prime(){ return new BPrime<>(); } } 的返回类型?以下解决方案可能适合您吗?

private interface C extends B {}

public void main(String[] args) {
  BPrime<C> prime = new BImpl<C>().prime(); // do you need this?
  BPrime<B> prime = new BImpl<>().prime(); // do you need this?


  BPrime prime = new BImpl().prime(); or is this sufficient...?
}

我想这个问题的答案在某种程度上是否需要以下内容:

{{1}}

答案 1 :(得分:1)

正如我在评论中所提到的,有很多地方类型params被省略,这使整个故事看起来不正确。

首先,你的Primes至少应该是这样的:

class EntityAPrime<T extends EntityA> extends AbstractPrime<T> {}

class EntityBPrime<T extends EntityB> extends EntityAPrime<T> {}

然后问题来到你的实体

class EntityA {
  public EntityAPrime<????> prime();  <- What should be the reasonable type param here?
}

class EntityB extends EntityA {
  public EntityBPrime<????> prime();  <- Same here
}

答案取决于您的设计。

我不知道你的第二选择是如何比原来的更糟糕(鉴于原来的那个是真正的凌乱)

你可以考虑这个(没有经过测试,根据你的设计,它是否正确,但至少类型参数对我来说是合理的):

public class Entity<T> {
  public Prime<T> prime();
}

public class AbstractEntityA<T extends EntityA> extends Entity<T> {
  @Override
  public AbstractAPrime<T> prime();
}

public class EntityA extends AbstractEntityA<EntityA>{
  public EntityAPrime prime() { ... }
}

public class EntityB extends AbstractEntityA<EntityB> {
  public EntityBPrime prime() {...}
}


public class AbstractAPrime<T extends EntityA> extends AbstractPrime<T> {}

public class EntityAPrime extends AbstractAPrime<EntityA>{}
public class EntityBPrime extends AbstractAPrime<EntityB>{}

简而言之,将原始EntityA中的内容移动到一个抽象类,由EntityA扩展(主要是普通扩展)和EntityB(覆盖并添加更多内容)。同样适用于Prime。