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