2016-12-13 3 views
1

Даже если это не очень хорошая практика, я хочу знать ради того, чтобы знать почему.Общее использование класса, вложенного в общий

Этот код не компилируется, и я не понимаю почему.


Скажем, у меня есть абстрактное определение Джокера, Королева и король:

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, королева и король

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) 

Однако, я не могу видеть как класс принцессы был бы другим.

Может ли кто-нибудь просветить меня?

ответ

1

Прежде всего, здесь есть только один класс Princess, который является QueenA.Princess. Нет QueenB.Princess - если вы пишете QueenB.Princess, то компилятор просто понимает это как QueenA.Princess. Обратите внимание, что поскольку Princess является нестационарным внутренним классом QueenA, общий класс, QueenA.Princess также является общим классом (параметризованным параметрами QueenA). Когда он параметризуется, он выглядит как QueenA<something>.Princess.

Поскольку проблема заключается в совместимости между двумя значениями класса Princess, и что мы отметили выше, существует один такой класс, единственная проблема совместимости - это аргумент общего типа. Тип, который передается в super(), предположительно Q.Princess. Но, как мы отметили выше, Princess относится к QueenA, а не QueenB или Q. Поэтому компилятор во время компиляции автоматически переписывает это на QueenA<something>.Princess. Вопрос в том, что такое something. В принципе, вопрос: Q - QueenA<what>. Q - это переменная типа с привязкой QueenB<?>. Это означает, что кто-то может использовать этот класс с Q, являющимся QueenB<X>, причем X является любым типом, который удовлетворяет границам параметра типа (а именно, который расширяет 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 (примечание: это Q, параметр другого типа, не путайте). Если вы будете следовать тому же анализу, что и выше, вы увидите, что компилятор видит, что это Q.Princess действительно означает QueenA<some unknown type that extends JokerA>.Princess.

Вопрос QueenA<first unknown subtype>.Princess Подтип QueenA<second unknown subtype>.Princess? то есть first unknown subtype то же, что и у second unknown subtype? (Помните, что генерики являются инвариантными.) Компилятор, просто глядя на это, скажет, что он не знает, являются ли они одинаковыми или нет, потому что два неизвестных типа могут быть разными, поэтому они несовместимы.

Можно сказать, постойте, вы знаете, что Q является QueenA<X> для некоторого неизвестного типа X, KingB<Q> объявляется продлить KingA<Q>, поэтому в Q в рамках KingB «s для конкретного объекта такой же, как Q в области KingB. Таким образом, Q s одинаковы, а неизвестный X в обоих случаях одинаковый. Но это не применяется здесь, потому что Q больше не рассматривается. Помните, что нет типа Q.Princess. Реальный тип - QueenA<something>.Princess. Это QueenA<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); 
    } 
} 
Смежные вопросы