Каковы формальные условия для параметра подстановочного знака в универсальном типе Java, чтобы быть в его пределах? - PullRequest
11 голосов
/ 10 августа 2011

С параметризованными типами в 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]), поэтому этот параметр находится в его пределах.(Но это не так!)

Следуя тому же рассуждению каждый подстановочный знак находится в пределах своих границ, поэтому что-то здесь не так ... Но где именно эти рассуждения пошли не так?Как делают эти правила при правильном применении?

1 Ответ

5 голосов
/ 10 августа 2011

JLS на дженериках неполна, и вы обнаружили в ней еще одну дыру.Нижняя граница для переменных типа почти не обсуждается, и я не вижу никаких ограничений в спецификации для X, имеющего верхнюю границу Number и нижнюю границу Runnable.Они, вероятно, не учли этого.

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

Легко проверить, составляют ли верхняя и нижняя границы пустой набор типов.Все супертипы нижней границы известны;по крайней мере один из них должен быть верхней границей, в противном случае в обеих границах нет типа.

-

Два случая Foo<? extends A> хорошо определены в спецификации.С захватом преобразования у нас есть новая переменная типа X с верхней границей A & Number, и спецификация говорит для верхней границы V1&...&Vm

Это ошибка времени компиляции, если для любых двухклассы (не интерфейсы) Vi и Vj, Vi не является подклассом Vj или наоборот.

Поэтому, если A = Thread, преобразование захвата завершается неудачей.

...