我有一个对象实例系统,其中包含对定义对象的引用。对于每个继承树,我都有一个顶级类。实例对象具有对相应定义类的通用引用。

在getter中使用泛型,顶级对象的子类可以获取正确的定义类型,而无需强制转换。但是,再次被子类化的抽象子类不能:

class Def { }

abstract class Animal<D extends Def> {
    D def;
    D getDef() { return def; }
}

class CatDef extends Def { }
class Cat extends Animal<CatDef> { }

abstract class BearDef extends Def { }
abstract class Bear<D extends BearDef> extends Animal<D> { }

class BlackBearDef extends BearDef { }
class BlackBear extends Bear<BlackBearDef> { }

class AnimalDefTest {
    public static void main (String... args) {
        Cat cat = new Cat();
        CatDef catDef = cat.getDef(); // CatDef works fine

        Bear bear = new BlackBear();
        BearDef bearDef = bear.getDef(); // Error: Expected Def not BearDef? Why???
        BearDef bearDef2 = ((Animal<BearDef>)bear).getDef(); // Works
    }
}

为什么getDef要求将Bear强制转换为(Animal<BearDef>)才能获得BearDef?最终将Bear定义为extends Animal<? extends BearDef>

[编辑]甚至更陌生,如果我更改Bear类行:
abstract class Bear<D extends BearDef> extends Animal<BearDef> { }

(在这种情况下,D未使用且无关紧要)仍然无效。擦除D并在下面的代码行中解决了上面代码中的错误(但并不能帮助我完成子类定义所需的工作):
abstract class Bear extends Animal<BearDef> { }

最佳答案

实际上,您正在使用原始类型Bear,但应使用BearDef类型对其进行参数化。如果你写

Bear<BlackBearDef> bear = new BlackBear();

或者
Bear<?> bear = new BlackBear();

会很好的。

另一件事:我不确定您打算如何使用它,但是在我看来,您可以这样做:
abstract class Bear extends Animal<BearDef> {}

class BlackBear extends Bear {
  // make use of covariant return type to make BlackBear return correct def
  @Override
  BlackBearDef getDef() { ... }
}

我想这的原因是,如果您想要一个Bear,其getDef()方法返回一个BlackBearDef,则该Bear引用将需要参数化为Bear<BlackBearDef>。在那种情况下(无论如何,对于您的示例),您实际上都知道它是声明类型的BlackBear,因此您也可以将其称为BlackBear。就是说,这显然并不总是那么简单,您的实际情况可能不允许这样做。

09-09 17:11