嵌套在泛型中的 class 的泛型使用

Generic use of a class nested in a generic

即使这不是一个好的做法,我也想知道是为了知道为什么。

下面的代码无法编译,我不太明白为什么。


假设我有一个小丑、女王和国王的抽象定义:

abstract class JokerA {
    //JokerA does things
}

abstract class QueenA<J extends JokerA> {
    //QueenA makes use of J
    class Princess {
        void receiveGift(Gift gift) {
            /* ... */
        }
    }
}

abstract class KingA<Q extends QueenA<?>> {
    KingA(Q.Princess princess) {
        Gift giftForPrincess = new Gift();
        princess.receiveGift(giftForPrincess);
    }
}

这很好用。 不过,我也想定义一个比较专业,但还是比较抽象的,Joker, Queen and King

abstract class JokerB extends JokerA {
    //JokerB does some things differently
}

abstract class QueenB<J extends JokerB> extends QueenA<J> {
    //QueenB makes use of J sometimes differently, because she knows how JokerBs behave
}

abstract class KingB<Q extends QueenB<?>> extends KingA<Q> {
    KingB(Q.Princess princess) {
        super(princess); //error
    }
}

错误是:

KingA(QueenA<capture<?>>.Princess) in KingA cannot be applied to (Q.Princess)

但是,我看不出公主 class 会有什么不同。

谁能教教我?

首先这里只有一个class名字叫Princess,就是QueenA.Princess。没有 QueenB.Princess——如果你写 QueenB.Princess,编译器会简单地将其理解为 QueenA.Princess。请注意,由于 PrincessQueenA 的非静态内部 class,泛型 class,QueenA.Princess 也是泛型 class(参数化通过 QueenA 的参数)。当它被参数化时,它看起来像 QueenA<something>.Princess.

由于问题是 class Princess 的两个值之间的兼容性,而我们在上面注意到有一个这样的 class,唯一的兼容性问题是关于通用的类型参数。传递给 super() 的类型应该是 Q.Princess。但是正如我们上面提到的,Princess 属于 QueenA,而不是 QueenBQ。所以编译器在编译时会自动将其重写为 QueenA<something>.Princess。问题是什么是something。基本上,问题是 Q 是一个 QueenA<what>Q 是一个绑定 QueenB<?> 的类型变量。这意味着有人可以使用此 class,QQueenB<X>X 是满足 QueenB 类型参数边界的任何类型(即扩展 JokerB)。所以我们不能对 X 做任何假设,除非它是 JokerB 的子类型。 QueenB<X> 扩展了 QueenA<X>,所以这意味着 Q 是一个 QueenA<X><capture ...> 只是编译器打印出来表示未知类型的东西。所以在这一点上,编译器已经计算出 Q.Princess 真正意味着 QueenA<some unknown type that extends JokerB>.Princess.

然后你把它传递给super()KingA的构造函数。这个类型参数的参数类型也写成Q.Princess(注意:这是KingAQ,一个不同的类型参数;不要混淆)。如果你按照上面同样的分析,你会看到编译器看到这个 Q.Princess 真的意味着 QueenA<some unknown type that extends JokerA>.Princess.

问题是,QueenA<first unknown subtype>.PrincessQueenA<second unknown subtype>.Princess的子类型吗?即 first unknown subtypesecond unknown subtype 相同吗? (记住泛型是不变的。)编译器只看这个会说它不知道它们是否相同,因为两个未知类型肯定可能不同,所以它们不兼容。

你可能会说,等一下,你知道 Q 是某个未知类型 XQueenA<X>KingB<Q> 被声明为扩展 KingA<Q>,因此 KingB 范围内特定对象的 QKingB 范围内的 Q 相同。所以 Q 是相同的,未知的 X 在两种情况下都是相同的。但这在这里不适用,因为 Q 不再被考虑。请记住,没有类型 Q.Princess。它表示的真实类型是QueenA<something>.PrincessQueenA<something>.Princess 是在编译时为每个 class 编译每个构造函数时计算出来的。一旦计算出是 unknown type,它就固定为 unknown type,并且与 class 的类型参数 Q 无关。所以这两个未知类型没有关系。

您可以通过使用新类型参数而不是让编译器推断未知类型来解决它。然后,类型参数可以让您连接该类型的不同用途,让编译器知道它们是同一类型。像这样:

abstract class KingA<J extends JokerA, Q extends QueenA<J>> {
    KingA(Q.Princess princess) {
        Gift giftForPrincess = new Gift();
        princess.receiveGift(giftForPrincess);
    }
}

abstract class KingB<J extends JokerB, Q extends QueenB<J>> extends KingA<J, Q> {
    KingB(Q.Princess princess) {
        super(princess);
    }
}