即使不是很好的做法,我也想知道原因。

下面的代码无法编译,我也不知道为什么。



假设我有一个小丑,皇后和国王的抽象定义:

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);
    }
}


这很好。
但是,我也想定义一个更加专业但仍然很抽象的小丑,皇后和国王

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)



但是,我看不出公主课会是什么样的人。

谁能启发我?

最佳答案

首先,这里只有一个名为Princess的类,即QueenA.Princess。没有QueenB.Princess-如果您编写QueenB.Princess,则编译器只是将其理解为QueenA.Princess。请注意,由于PrincessQueenA的非静态内部类(泛型类),因此QueenA.Princess也是泛型类(由QueenA的参数进行了参数化)。参数化后,它看起来像QueenA<something>.Princess

由于问题是类Princess的两个值之间的兼容性,而我们上面已提到有一个这样的类,因此唯一的兼容性问题是有关泛型类型参数的。传递给super()的类型应该是Q.Princess。但是如上所述,Princess属于QueenA,而不是QueenBQ。因此,编译器在编译时会自动将其重写为QueenA<something>.Princess。问题是什么是something。基本上,问题是QQueenA<what>Q是绑定了QueenB<?>的类型变量。这意味着某人可以使用QQueenB<X>X为满足QueenB类型参数范围的任何类型(即扩展JokerB的类型)的此类。因此,除了X的子类型之外,我们无法假设有关JokerB的任何内容。 QueenB<X>扩展QueenA<X>,这意味着QQueenA<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是某些未知类型QueenA<X>XKingB<Q>被声明为扩展KingA<Q>,因此QKingB范围内特定对象与Q范围内的KingB相同。因此,Q是相同的,未知的X在两种情况下都是相同的。但这不适用于这里,因为不再考虑Q了。请记住,没有类型Q.Princess。它表示的实际类型是QueenA<something>.PrincessQueenA<something>.Princess是在编译时为每个类编译每个构造函数时弄清楚的。一旦确定为unknown type,它将固定为unknown type,并且与作为类的类型参数的Q不相关。因此,这两个未知类型没有关系。

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

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);
    }
}

10-01 18:43