С параметризованными типами в Java, как правила, которые проверяют, находится ли параметр в пределах его привязки, работают именно для подстановочных знаков?
Для класса, подобного этому:
class Foo<T extends Number> {}
Экспериментируя с тем, что принимает компилятор, узнает, что:
- Подстановочный знак A
? extends
с использованием несвязанного типа интерфейса разрешен:Foo<? extends Runnable>
действителен - Подстановочный знак A
? extends
с использованием несвязанного типа класса недопустим:Foo<? extends Thread>
недействителен. Это имеет смысл, потому что ни один тип не может быть подтипом какNumber
, так иThread
- В подстановочном знаке
? super
нижняя граница в подстановочном знаке должна быть подтипом границы переменной типа:Foo<? super Runnable>
не допускается, потому чтоRunnable
не является подтипомNumber
. Опять же, это ограничение имеет смысл.
Но где эти правила определены? Глядя на Раздел 1.4 языка Java, я не вижу ничего отличительного интерфейса от классов; и при применении моей интерпретации JLS Foo<? super Runnable>
считается действительным. Поэтому я, вероятно, что-то неправильно понял. Здесь моя попытка:
В этом разделе JLS:
Параметрированный тип состоит из имени класса или интерфейса C и списка аргументов фактического типа < T1,..., Tn > . Это ошибка времени компиляции, если C не является именем универсального класса или интерфейса, или если количество аргументов типа в списке аргументов фактического типа отличается от числа объявленных параметров типа C. В следующем, всякий раз, когда мы говорим класса или типа интерфейса, мы включаем также общую версию, если явно не исключено. Всюду в этом разделе пусть A1,..., An - формальные параметры типа C, и пусть Bi - объявленная граница Ai. Обозначение [Ai: = Ti] означает замену переменной типа Ai типом Ti, для 1 <= я <= n, и используется в этой спецификации.
Пусть P = G < T1,..., Tn > - параметризованный тип. Должно быть, что после того, как P подвергается преобразованию захвата (§ 5.1.10), что приводит к типу G < X1,..., Xn > для каждого аргумента фактического типа Xi, 1 <= я <= = n, Xi <: Bi [A1: = X1,..., An: = Xn] (п. 4.10), или возникает ошибка времени компиляции.
Примените это к P = Foo<? super Runnable>
: это дает C = Foo
, n = 1, T1 = ? super Runnable
и B1 = Number
.
Для преобразования захвата эта часть определения преобразования захвата применяется:
Если Ti является аргументом типа подстановочной формы формы? супер Bi, то Si представляет собой новую переменную типа, верхняя граница которой Ui [A1: = S1,..., An: = Sn] и нижняя граница которой Bi.
Это дает G < X1,..., Xn >= Foo<X>
, где X
является переменной нового типа с верхней границей Number
и нижней границей Runnable
. Я не вижу ничего явно запрещающего такую переменную типа.
В B1 = Number
нет переменных типа, поэтому Bi [A1: = X1,..., An: = Xn] по-прежнему просто Number
.
X
имеет Number
как верхнюю границу (исходящую от преобразования захвата) и согласно правила подтипирования "Прямые супертипы типа переменная - это типы, перечисленные в ее привязке", поэтому X
<: Number
(= Bi [A1: = X1,..., An: = Xn]), поэтому этот параметр находится в пределах его границ. (Но это не так!)
Следуя тем же рассуждениям, что каждый шаблон находится в пределах его границ, так что что-то здесь не так... Но где именно это рассуждение пошло не так? Как эти правила работают при правильном применении?