Проблема преобразования захвата в Java, согласование WRT JLS и фактическое поведение JDK
Учитывая следующие два определения класса:
class C1<T extends C1<T>> {}
class C2<U> extends C1<C2<U>> {}
Рассмотрим следующее объявление типа:
C1<? extends C2<String>> c;
Это компилируется в JDK-8u45, но если мы рассмотрим спецификацию для преобразования захвата, мне кажется (что) это объявление должно привести к времени компиляции ошибка.
В частности, верхняя граница захвата переменной типа T#1
задается как glb(Bi, Ui[A1:=S1,...,An:=Sn])
, где в этом случае Bi
разрешает привязку подстановочного символа C2<String>
и Ui[A1:=S1,...,An:=Sn]
разрешается до C1<T#1>
.
Из этого, glb(C2<?>, C1<T#1>)
разрешается тип пересечения C2<String> & C1<T#1>
, что является недопустимым, поскольку C2<String>
и C1<T#1>
- оба типа классов, а не типы интерфейсов, но ни один из них не является подтипом другого.
Это (очевидное) нарушение правила, возможно, более ясно описано в определении самого типа пересечения.
Я уверен, что это не ошибка, и я просто делаю некоторые простые ошибки где-то... Если это ошибка, я надеюсь, что это можно считать ошибкой в JLS, а не JDK, чтобы я мог надеяться на безопасное подражание поведению...
Спасибо за любую помощь!
Изменить: Вчера, после разговора с Radiodef, я убедился, что проблема (или один из способов взглянуть на нее по крайней мере) заключается в том, что C2<String>
можно эффективно рассматривать как подтип C1<T#1>
, так как T # 1 может удовлетворяться только C2<String>
и поэтому может считаться равным ему, но правила сдерживания и подтипирования не имеют понимания этого отношения как написано, и поэтому JLS не будет распознавать подтип и должен не в состоянии...
Если вы делаете несколько более сложный случай C1<? extends C2<?>> d;
, тем не менее, это более сложно. Проблема аналогична, но тип пересечения, который образует верхнюю границу захвата, выражается как C2<?> & C1<T#2>
, где, похоже, решение не может быть получено теми же рассуждениями, что и выше.
Ответы
Ответ 1
На этот вопрос лучше ответить ответ Maurizio на компилятор-dev.
(TL; DR javac действительно не согласуется с спецификацией здесь, и оптимальное решение, вероятно, лежит где-то между двумя подходами)
Связанная ошибка можно найти здесь.
Большое спасибо всем, кто внес свой вклад в этот ответ.