Как в случае с параметризованными типами в Java правила, проверяющие, находится ли параметр в пределах границ, работают точно для подстановочных знаков?
Учитывая такой класс:
class Foo<T extends Number> {}
Экспериментируя с тем, что принимает компилятор, мы узнаем, что:
- Допускается использование подстановочного знака
? extends
с несвязанным типом интерфейса:Foo<? extends Runnable>
является допустимым. - Подстановочный знак
? extends
с использованием несвязанного типа класса не допускается:Foo<? extends Thread>
недействителен. Это имеет смысл, потому что ни один тип не может быть подтипом одновременноNumber
иThread
. - В подстановочном знаке
? super
нижняя граница подстановочного знака должна быть подтипом границы переменной типа:Foo<? super Runnable>
не допускается, посколькуRunnable
не является подтипомNumber
. Опять же, это ограничение имеет смысл.
Но где определены эти правила? Глядя на раздел 4.5 Java Language Specification, Я не вижу ничего, что бы отличало интерфейсы от классов; и при применении моей интерпретации JLS Foo<? super Runnable>
считается действительным. Так что, наверное, я что-то не так понял. Вот моя попытка:
Из этого раздела JLS:
Параметризованный тип состоит из имени класса или интерфейса C и списка аргументов фактического типа ‹T1, ..., Tn>. Это ошибка времени компиляции, если C не является именем универсального класса или интерфейса или если количество аргументов типа в фактическом списке аргументов типа отличается от количества объявленных параметров типа C. В дальнейшем, всякий раз, когда мы говорим класса или типа интерфейса, мы также включаем универсальную версию, если это явно не исключено. Всюду в этом разделе пусть A1 , ... , An будут формальными параметрами типа C, и пусть Bi будет объявленной границей Ai. Обозначение [Ai := Ti] обозначает замену переменной типа Ai на тип Ti для 1 ‹= i ‹= n и используется в этой спецификации.
Пусть P = G‹T1,...,Tn> — параметризованный тип. Должен быть тот случай, когда P подвергается преобразованию захвата (§5.1.10), приводящему к типу G‹X1, ..., Xn>, для каждого фактического аргумента типа Xi, 1 ‹= i ‹= n , Xi ‹: Bi[A1 := X1, ..., An := Xn] (§4.10), или возникает ошибка времени компиляции.
Примените это к P = Foo<? super Runnable>
: это дает C = Foo
, n = 1, T1 = ? super Runnable
и B1 = Number
.
Для преобразования захвата эта часть определения преобразования захвата применяется:
Если Ti является аргументом подстановочного знака формы ? super 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 ]), поэтому этот параметр находится в допустимых пределах. (Но это не так!)
Следуя тем же рассуждениям, каждый подстановочный знак находится в пределах своих границ, так что здесь что-то не так... Но где именно это рассуждение пошло не так? Как работают эти правила при правильном применении?