Spec-Zone.ru › Java Language Specification 11

Глава 18. Вывод типов

Оглавление

18.1. Понятия и обозначения
18.1.1. Переменные вывода
18.1.2. Формулы ограничений
18.1.3. Границы
18.2. Сведение
18.2.1. Ограничения совместимости выражений
18.2.2. Ограничения совместимости типов
18.2.3. Ограничения подтипирования
18.2.4. Ограничения равенства типов
18.2.5. Ограничения исключений, обрабатываемых во время выполнения
18.3. Включение
18.3.1. Дополняющие пары границ
18.3.2. Границы, включающие преобразование захвата
18.4. Разрешение
18.5. Использование вывода
18.5.1. Вывод применимости вызова
18.5.2. Вывод типа вызова
18.5.2.1. Совместимость вызова поли метода
18.5.2.2. Дополнительные ограничения аргументов
18.5.3. Вывод параметризации функционального интерфейса
18.5.4. Вывод наиболее специфичного метода

Различные анализы во время компиляции требуют рассуждений о типах, которые ещё неизвестны. К ним относятся, прежде всего, проверка применимости методов дженериков (§18.5.1) и вывод типа вызова дженериков (§18.5.2). В общем случае, процесс рассуждений о неизвестных типах называется выводом типов.

В общих чертах, вывод типов можно разделить на три процесса:

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

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

  • Разрешение рассматривает границы переменной вывода и определяет инстанцирование, совместимое с этими границами. Он также решает порядок разрешения взаимозависимых переменных вывода.

Эти процессы тесно взаимодействуют: сведение может инициировать включение; включение может привести к дальнейшему сведению; а разрешение может вызвать дальнейшее включение.

  • §18.1 более точно определяет используемые понятия в качестве промежуточных результатов и обозначения для их выражения.

  • §18.2 подробно описывает сведение.

  • §18.3 подробно описывает включение.

  • §18.4 подробно описывает разрешение.

  • §18.5 определяет, как эти инструменты вывода используются для решения определённых задач анализа во время компиляции.

По сравнению с Java SE 7 Edition Спецификации языка Java®, важные изменения в выводе типов включают:

  • Добавление поддержки лямбда-выражений и ссылок на методы в качестве аргументов вызова методов.

  • Обобщение для определения вывода в терминах поли-выражений, которые могут не иметь хорошо определённых типов до после завершения вывода. Это существенно улучшает вывод для вложенных дженериков, методов и диамантных вызовов конструкторов.

  • Описание того, как вывод используется для обработки типов-целей функционального интерфейса с подстановкой шаблонов и анализа наиболее специфичного метода.

  • Уточнение различия между проверкой применимости вызова (которая включает только аргументы вызова) и выводом типа вызова (который включает тип цели).

  • Отложенное разрешение всех переменных вывода, даже тех, которые имеют нижние границы, до вывода типа вызова, для получения лучших результатов.

  • Улучшение поведения вывода для взаимозависимых (или самозависимых) переменных.

  • Устранение ошибок и потенциальных источников путаницы. Данная редакция более тщательно и точно обрабатывает различие между контекстами конкретного преобразования и подтипированием, и описывает сведение, параллельно соответствующим не-выводящим отношениям. Там, где есть преднамеренные отклонения от не-выводящих отношений, они явно идентифицированы как таковые.

  • Заложение основы для будущего развития: улучшения или новые приложения вывода будут проще интегрировать в спецификацию.

18.1. Понятия и обозначения

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

18.1.1. Переменные вывода

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

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

18.1.2. Формулы ограничений

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

  • ‹Выражение → T›: Выражение совместимо в контексте вызова с типом T (§5.3).

  • ‹S → T›: Тип S совместим в контексте вызова с типом T (§5.3).

  • ‹S ≤ T›: Ссылочный тип S является подтипом ссылочного типа T (§4.10).

  • ‹S ⊂ T›: Аргумент типа S содержится в аргументе типа T (§4.5.1).

  • ‹S = T›: Тип S совпадает с типом T (§4.3.4), или аргумент типа S совпадает с аргументом типа T.

  • ‹LambdaExpression →throws T›: Проверяемые исключения, бросаемые телом LambdaExpression, объявлены в пункте throws типа функции, полученного из T.

  • ‹MethodReference →throws T›: Проверяемые исключения, бросаемые ссылаемым методом, объявлены в пункте throws типа функции, полученного из T.

Примеры формул ограничений:

  • Из Collections.singleton("hi"), у нас есть формула ограничения ‹"hi" → α›. После упрощения она станет формулой ограничения: ‹String <: α›.

  • Из Arrays.asList(1, 2.0), у нас есть формулы ограничений ‹1 → α› и ‹2.0 → α›. После упрощения они станут формулами ограничений ‹int → α› и ‹double → α›, а затем ‹Integer <: α› и ‹Double <: α›.

  • Из целевого типа вызова конструктора List<Thread> lt = new ArrayList<>(), у нас есть формула ограничения ‹ArrayList<α> → List<Thread>›. После упрощения она станет формулой ограничения ‹α <= Thread›, а затем ‹α = Thread›.

18.1.3. Границы

В процессе вывода сохраняется набор границ на переменных вывода. Граница имеет одну из следующих форм:

  • S = T, где хотя бы одна из S или T является переменной вывода: S совпадает с T.

  • S <: T, где хотя бы одна из S или T является переменной вывода: S является подтипом T.

  • false: Не существует допустимого выбора переменных вывода.

  • G<α1, ..., αn> = capture(G<A1, ..., An>): Переменные α1, ..., αn представляют результат преобразования захвата (§5.1.10) применённого к G<A1, ..., An> (где A1, ..., An могут быть типами или условными знаками и могут содержать переменные вывода).

  • throws α: Переменная вывода α появляется в throws-клаузуле.

Граница выполняется заменой переменных вывода, если после применения замены утверждение является истинным. Граница false никогда не может быть выполнена.

Некоторые границы связывают переменную вывода с типом. Пусть T является типом. В случае границы в форме α = T или T = α мы говорим, что T является инстанциацией α. Аналогично, в случае границы в форме α <: T мы говорим, что T является верхней границей α, а в случае границы в форме T <: α мы говорим, что T является нижней границей α.

Другие границы связывают две переменные вывода или переменную вывода с типом, содержащим переменные вывода. Такие границы в форме S = T или S <: T называются зависимостями.

Граница в форме G<α1, ..., αn> = capture(G<A1, ..., An>) указывает, что α1, ..., αn являются заполнителями для результатов преобразования захвата. Это необходимо, потому что преобразование захвата может быть выполнено только над типом, а переменные вывода в A1, ..., An могут ещё не быть разрешены.

Граница в форме throws α является чисто информационной: она направляет разрешение на оптимизацию инстанциации α таким образом, чтобы, если это возможно, она не была типом проверяемого исключения.

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

Примеры множеств границ:

  • { α = String } содержит одну границу, инстанциирующую α как String.

  • { Integer <: α, Double <: α, α <: Object } описывает две нижние границы и одну верхнюю границу для α.

  • { α <: Iterable<?>, β <: Object, α <: List<β> } описывает верхнюю границу для каждого из α и β, а также зависимость между ними.

  • { } не содержит границ и зависимостей и может быть названо true.

  • { false } указывает на то, что не существует удовлетворительной инстанциации.

Когда вывод начинается, множество границ обычно генерируется из списка объявлений параметров типа P1, ..., Pp и связанных с ними переменных вывода α1, ..., αp. Это множество границ генерируется следующим образом. Для каждого l (1 ≤ l ≤ p):

  • Если у Pl нет TypeBound, в множество добавляется граница αl <: Object.

  • В противном случае, для каждого типа T, ограниченного & в TypeBound, в множество добавляется граница αl <: T[P1:=α1, ..., Pp:=αp]; если это не приводит к появлению верхней границы для αl (только зависимости), то в множество добавляется граница αl <: Object.

18.2. Редукция

Редукция — это процесс упрощения набора формул ограничений (§18.1.2) для получения множества границ (§18.1.3).

Каждая формула ограничения рассматривается по очереди. Правила в этом разделе определяют, как формула сводится к одному или обоим из:

  • Границе или множеству границ, которые должны быть включены в "текущее" множество границ. Изначально текущее множество границ пустое.

  • Дополнительные формулы ограничений, которые должны быть редуцированы рекурсивно.

Редукция завершается, когда больше нет формул ограничений для редукции.

Результаты шага редукции всегда сохраняют корректность: если инстанциация переменной вывода удовлетворяет редуцированным ограничениям и границам, она также будет удовлетворять исходному ограничению. С другой стороны, редукция не сохраняет полноту: могут существовать инстанциации переменных вывода, которые удовлетворяют исходному ограничению, но не удовлетворяют редуцированному ограничению или границе. Это обусловлено присущими ограничениями алгоритма, а также желанием избежать чрезмерной сложности. Одним из последствий является то, что существуют выражения, для которых вычисление типов аргументов не находит решения, но которые могут быть корректными, если программист явно вставляет соответствующие типы.

18.2.1. Ограничения совместимости выражений

Формула ограничения вида ‹Выражение → T› сокращается следующим образом:

  • Если T является собственным типом, ограничение сокращается до true, если выражение совместимо в контексте вызова с ослабленными условиями с T (§5.3), и false в противном случае.

  • В противном случае, если выражение является автономным выражением (§15.2) типа S, ограничение сокращается до ‹S → T›.

  • В противном случае, выражение является поливыражением (§15.2). Результат зависит от формы выражения:

    • Если выражение является выражением в скобках вида ( Выражение' ), ограничение сокращается до ‹Выражение' → T›.

    • Если выражение является выражением создания экземпляра класса или выражением вызова метода, ограничение сокращается до набора связей B3, который используется для определения совместимости выражения с целевым типом T, как определено в §18.5.2.1. (Для выражения создания экземпляра класса соответствующий "метод" для вывода определен в §15.9.3.)

      Этот набор связей может содержать новые переменные вывода, а также зависимости между этими новыми переменными и переменными вывода в T.

    • Если выражение является условным выражением вида e1 ? e2 : e3, ограничение сокращается до двух формул ограничений ‹e2 → T› и ‹e3 → T›.

    • Если выражение является лямбда-выражением или выражением ссылки на метод, результат указан ниже.

Обращаясь к вложенным вызовам обобщенных методов как к поливыражениям, мы улучшаем поведение вывода для вложенных вызовов. Например, следующее не является законным в Java SE 7, но является законным в Java SE 8:

ProcessBuilder b = new ProcessBuilder(Collections.emptyList());
  // ProcessBuilder's constructor expects a List<String>

Когда оба внешний и вложенный вызов требуют вывода, проблема усложняется. Например:

List<String> ls = new ArrayList<>(Collections.emptyList());

Наш подход заключается в «подъеме» ограничений, выведенных для вложенного вызова (просто { α <: Object } в случае emptyList) в процесс внешнего вывода (в данном случае, попытка вывести β, где конструктор относится к типу ArrayList<β>). Мы также выводим зависимости между вложенными переменными вывода и внешними переменными вывода (ограничение ‹List<α> → Collection<β>› приводилось бы к зависимости α = β). Таким образом, разрешение переменных вывода во вложенном вызове может подождать, пока не будут получены дополнительные сведения от внешнего вызова (на основе целевого присваивания, β = String).

Формула ограничения вида ‹LambdaExpression → T›, где T упоминает по крайней мере одну переменную вывода, сокращается следующим образом:

  • Если T не является типом функционального интерфейса (§9.8), ограничение сокращается до false.

  • В противном случае, пусть T' будет базовым целевым типом, полученным из T, как указано в §15.27.3. Если используется §18.5.3 для получения параметризованного типа функционального интерфейса, то проверка того, что F<A'1, ..., A'm> является подтипом F<A1, ..., Am> не выполняется (вместо этого, она утверждается с помощью формулы ограничения ниже). Пусть целевой тип функции для лямбда-выражения — это тип функции T'. Затем:

    • Если не найдено допустимого типа функции, ограничение сокращается до false.

    • В противном случае, конгруэнтность LambdaExpression с целевым типом функции утверждается следующим образом:

      • Если число параметров лямбда-выражения отличается от числа типов параметров типа функции, ограничение сокращается до false.

      • Если лямбда-выражение неявно типизировано и один или несколько типов параметров типа функции не являются собственными типами, ограничение сокращается до false.

        Это условие никогда не возникает на практике из-за обработки неявно типизированных лямбда-выражений в §18.5.1 и подстановки, применяемой к целевому типу в §18.5.2.2.

      • Если результат типа функции — void, а тело лямбда-выражения не является выражением-оператором и не является блоком, совместимым с void, ограничение сокращается до false.

      • Если результат типа функции не void, а тело лямбда-выражения — блок, который не является совместимым с типом значения, ограничение сокращается до false.

      • В противном случае, ограничение сокращается до всех следующих формул ограничений:

        • Если параметры лямбда-выражения имеют явно объявленные типы F1, ..., Fn, а тип функции имеет типы параметров G1, ..., Gn, то (i) для всех i (1 ≤ i ≤ n), ‹Fi = Gi›, и (ii) ‹T' <: T›.

        • Если тип возвращаемого значения типа функции является (не-void) типом R, предположим, что типы параметров лямбда-выражения такие же, как типы параметров типа функции. Тогда:

          • Если R является собственным типом, и если тело лямбда-выражения или какое-либо выражение результата в теле лямбда-выражения не совместимо в контексте присваивания с R, то false.

          • В противном случае, если R не является собственным типом, то в случае, когда тело лямбда-выражения имеет вид Выражение, ограничение ‹Выражение → R›; или если тело лямбда-выражения является блоком с выражениями результата e1, ..., em, для всех i (1 ≤ i ≤ m), ‹ei → R›.

Ключевая информация, которую нужно получить из формулы совместимости, связанной с лямбда-выражением, — это набор ограничений на переменные вывода, присутствующие в типе возвращаемого значения целевого типа функции. Это имеет решающее значение, потому что функциональные интерфейсы часто обобщены, а многие методы, работающие с этими типами, также обобщены.

В самом простом случае лямбда-выражение может просто предоставить нижнюю границу для переменной вывода:

<T> List<T> makeThree(Factory<T> factory) { ... }
String s = makeThree(() -> "abc").get(2);

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

Большая часть работы, описанной в этом разделе, предшествует утверждениям об выражениях результата; её цель состоит в выводе типа функции лямбда-выражения и проверке выражений, которые явно не соответствуют условиям совместимости.

Мы не пытаемся определить границы для переменных вывода, которые появляются в пункте throws типа целевой функции. Это связано с тем, что обработка исключений не является частью совместимости (§15.27.3) — в частности, она не должна влиять на применимость метода (§18.5.1). Однако мы получаем границы для этих переменных позже, потому что вывод типа вызова (§18.5.2.2) создаёт формулы ограничений на обработку исключений (§18.2.5).

Обратите внимание, что если целевой тип является переменной вывода или если типы параметров целевого типа содержат переменные вывода, мы получаем false. Во время вывода типа вызова (§18.5.2.2) выполняются дополнительные подстановки для приведения этих переменных вывода к конкретным типам, тем самым избегая этой ситуации. (Другими словами, сокращение на практике никогда не будет «вызвано» с целевым типом одного из этих видов.)

Наконец, обратите внимание, что выражения результата лямбда-выражения должны, согласно §15.27.3, быть совместимы в контексте присваивания с возвращаемым типом целевого типа, R. Если R является корректным типом, например, Byte, полученным из Function<α,Byte>, то проверка присваиваемости достаточно проста, и сокращение выполняет её выше. Если R не является корректным типом, например, α, полученным из Function<String,α>, то мы делаем выше упрощающее предположение, что достаточной будет слабая совместимость вызова. Разница между совместимостью присваивания и слабой совместимостью вызова заключается в том, что только присваивание допускает сужение константных выражений, таких как Byte b = 100;. Следовательно, наше упрощающее предположение не сохраняет полноту: задан целевой возвращаемый тип α и выражение результата целого числа 100, можно представить, что α может быть приведён к Byte, но сокращение фактически не создаст такого ограничения.

Формула ограничения вида ‹СсылкаНаМетод → T›, где T содержит по меньшей мере одну переменную вывода, сокращается следующим образом:

  • Если T не является типом функционального интерфейса или если T является типом функционального интерфейса, но не имеет типа функции (§9.9), ограничение сокращается до false.

  • В противном случае, если для ссылки на метод не существует потенциально применимого метода при нацеливании на T, ограничение сокращается до false.

  • В противном случае, если ссылка на метод точная (§15.13.1), пусть P1, ..., Pn будут типами параметров типа функции T, а F1, ..., Fk — типами параметров потенциально применимого метода. Ограничение сокращается до нового набора ограничений следующим образом:

    • В особом случае, когда n = k+1, параметр типа P1 должен выступать в качестве целевой ссылки вызова. Выражение ссылки на метод необходимо иметь вид ТипСсылка :: [АргументыТипов] Идентификатор. Ограничение сокращается до ‹P1 <: ТипСсылка› и для всех i (2 ≤ i ≤ n), ‹Pi → Fi-1›.

      Во всех остальных случаях n = k, и ограничение сокращается до, для всех i (1 ≤ i ≤ n), ‹Pi → Fi›.

    • Если результат типа функции не void, пусть R будет его возвращаемым типом. Тогда, если результат потенциально применимого объявления на этапе компиляции void, ограничение сокращается до false. В противном случае, ограничение сокращается до ‹R' → R›, где R' — результат применения преобразования захвата (§5.1.10) к возвращаемому типу потенциально применимого объявления на этапе компиляции.

  • В противном случае, ссылка на метод неточная, и:

    • Если один или несколько типов параметров типа функции не являются корректными типами, ограничение сокращается до false.

      Это условие никогда не возникает на практике из-за обработки неточных ссылок на методы в §18.5.1 и подстановки, применённой к целевому типу в §18.5.2.2.

    • В противном случае, выполняется поиск объявления на этапе компиляции, как указано в §15.13.1. Если для ссылки на метод нет объявления на этапе компиляции, ограничение сокращается до false. В противном случае, существует объявление на этапе компиляции, и: (пусть R — результат типа функции)

      • Если R void, ограничение сокращается до true.

      • В противном случае, если выражение ссылки на метод опускает АргументыТипов, и объявление на этапе компиляции является обобщённым методом, и возвращаемый тип объявления на этапе компиляции содержит по меньшей мере один из параметров типа метода, тогда:

        • Если R содержит один из параметров типа метода, ограничение сокращается до false.

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

        • Если R не содержит один из параметров типа метода, то ограничение сокращается до набора ограничений B3, которое будет использоваться для определения совместимости ссылки на метод при нацеливании на возвращаемый тип типа функции, как определено в §18.5.2.1. B3 может содержать новые переменные вывода, а также зависимости между этими новыми переменными и переменными вывода в T.

          Стратегия определения возвращаемого типа обобщённого метода, на который ссылаются, аналогична стратегии, используемой ранее в этом разделе для вызовов обобщённых методов. Это может включать «поднятие» ограничений в внешний контекст и вывод зависимостей между двумя наборами переменных вывода.

      • В противном случае, пусть R' — результат применения преобразования захвата (§5.1.10) к возвращаемому типу типа вызова (§15.12.2.6) объявления на этапе компиляции. Если R' void, ограничение сокращается до false; в противном случае, ограничение сокращается до ‹R' → R›.

18.2.2. Ограничения совместимости типов

Формула ограничения вида ‹S → T› сокращается следующим образом:

  • Если S и T являются правильными типами, ограничение сводится к true, если S совместим в контексте вызова с ослабленными требованиями с T (§5.3), и к false в противном случае.

  • В противном случае, если S является примитивным типом, пусть S' будет результатом применения преобразования упаковки (§5.1.7) к S. Тогда ограничение сводится к ‹S' → T›.

  • В противном случае, если T является примитивным типом, пусть T' будет результатом применения преобразования упаковки (§5.1.7) к T. Тогда ограничение сводится к ‹S = T'›.

  • В противном случае, если T является параметризованным типом вида G<T1, ..., Tn>, и не существует типа вида G<...>, являющегося супертипом S, но исходный тип G является супертипом S, тогда ограничение сводится к true.

  • В противном случае, если T является типом массива вида G<T1, ..., Tn>[]k, и не существует типа вида G<...>[]k, являющегося супертипом S, но исходный тип G[]k является супертипом S, тогда ограничение сводится к true. (Обозначение []k указывает на тип массива с k измерениями.)

  • В противном случае, ограничение сводится к ‹S <: T›.

Четвёртый и пятый случаи являются неявными использованиями неконтролируемого преобразования (§5.1.9). Эти случаи, наряду с любым использованием неконтролируемого преобразования в первом случае, могут привести к предупреждениям о неконтролируемых ошибках во время компиляции и могут повлиять на тип вызова метода (§15.12.2.6).

Упаковка T в T' не сохраняет полноту; например, если T было бы long, S могло бы быть установлено на Integer, что не является подтипом Long, но может быть распаковано и затем расширено до long. Мы избегаем этой проблемы в большинстве случаев, предоставляя специальное обращение к типам возврата переменных вывода, которые, как мы знаем, уже ограничены определёнными упакованными примитивными типами; см. §18.5.2.1.

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

18.2.3. Ограничения суботипизации

Формула ограничения вида ‹S <: T› сокращается следующим образом:

  • Если S и T — это правильные типы, ограничение сводится к истинно, если S является подтипом T (§4.10), и к ложно в противном случае.

  • В противном случае, если S — это нулевой тип, ограничение сводится к истинно.

  • В противном случае, если T — это нулевой тип, ограничение сводится к ложно.

  • В противном случае, если S — это переменная вывода, α, ограничение сводится к ограничению α <: T.

  • В противном случае, если T — это переменная вывода, α, ограничение сводится к ограничению S <: α.

  • В противном случае, ограничение сокращается в соответствии с формой T:

    • Если T — это параметризованный класс или интерфейс, или тип внутреннего класса параметризованного класса или интерфейса (прямо или косвенно), пусть A1, ..., An — аргументы типа T. Среди надтипов S идентифицируется соответствующий класс или интерфейс с аргументами типа B1, ..., Bn. Если такого типа не существует, ограничение сводится к ложно. В противном случае ограничение сводится к следующим новым ограничениям: для всех i (1 ≤ i ≤ n), ‹Bi <= Ai›.

    • Если T — это любой другой тип класса или интерфейса, то ограничение сводится к истинно, если T входит в число надтипов S, и к ложно в противном случае.

    • Если T — тип массива, T'[], то среди надтипов S, являющихся типами массивов, идентифицируется наиболее конкретный тип, S'[] (это может быть сам S). Если такого типа массива не существует, ограничение сводится к ложно. В противном случае:

      • Если ни S', ни T' не являются примитивным типом, ограничение сводится к ‹S' <: T'›.

      • В противном случае, ограничение сводится к истинно, если S' и T' являются одним и тем же примитивным типом, и к ложно в противном случае.

    • Если T — переменная типа, существуют три случая:

      • Если S — это пересечение типов, элементом которого является T, ограничение сводится к истинно.

      • В противном случае, если у T есть нижняя граница, B, ограничение сводится к ‹S <: B›.

      • В противном случае, ограничение сводится к ложно.

    • Если T — это пересечение типов, I1 & ... & In, ограничение сводится к следующим новым ограничениям: для всех i (1 ≤ i ≤ n), ‹S <: Ii›.

Формула ограничения вида ‹S <= T›, где S и T — аргументы типа (§4.5.1), сокращается следующим образом:

  • Если T — это тип:

    • Если S — это тип, ограничение сводится к ‹S = T›.

    • Если S — это подстановка, ограничение сводится к ложно.

  • Если T — это подстановка вида ?, ограничение сводится к истинно.

  • Если T — это подстановка вида ? extends T':

18.2.4. Ограничения равенства типов

Формула ограничения вида ‹S = T›, где S и T — типы, сокращается следующим образом:

  • Если S и T — правильные типы, ограничение сокращается до true, если S совпадает с T (§4.3.4), и до false в противном случае.

  • В противном случае, если S или T — нулевой тип, ограничение сокращается до false.

  • В противном случае, если S — переменная вывода, α, а T — не примитивный тип, ограничение сокращается до связи α = T.

  • В противном случае, если T — переменная вывода, α, а S — не примитивный тип, ограничение сокращается до связи S = α.

  • В противном случае, если S и T — типы классов или интерфейсов с одинаковой стираемой формой, где S имеет аргументы типа B1, ..., Bn, а T имеет аргументы типа A1, ..., An, ограничение сокращается до следующих новых ограничений: для всех i (1 ≤ i ≤ n), ‹Bi = Ai›.

  • В противном случае, если S и T — массивы, S'[] и T'[], ограничение сокращается до ‹S' = T'›.

  • В противном случае, если S и T — пересекающиеся типы, устанавливается соответствие между элементами S и элементами T. Элемент S, Si, соответствует элементу T, Tj, если Si и Tj являются либо одним и тем же типом, либо параметризациями одного и того же обобщённого класса или интерфейса, либо массивами.

    Если каждому элементу S соответствует ровно один элемент T, и наоборот, то ограничение сокращается до следующих новых ограничений: для каждого элемента Si из S и соответствующего элемента Tj из T, ‹Si = Tj›. В противном случае, ограничение сокращается до false.

    Это правило не учитывает переменные вывода, появляющиеся непосредственно как элементы пересекающегося типа (а не вложенные в параметризованный тип). Из-за ограничений на объявления параметров типа (§4.4) такие пересекающиеся типы не возникают на практике.

  • В противном случае, ограничение сокращается до false.

Формула ограничения вида ‹S = T›, где S и T — аргументы типа (§4.5.1), сокращается следующим образом:

  • Если S и T — типы, ограничение сокращается, как описано выше.

  • Если S имеет вид ?, а T имеет вид ?, ограничение сокращается до true.

  • Если S имеет вид ?, а T имеет вид ? extends T', ограничение сокращается до ‹Object = T'›.

  • Если S имеет вид ? extends S', а T имеет вид ?, ограничение сокращается до ‹S' = Object›.

  • Если S имеет вид ? extends S', а T имеет вид ? extends T', ограничение сокращается до ‹S' = T'›.

  • Если S имеет вид ? super S', а T имеет вид ? super T', ограничение сокращается до ‹S' = T'›.

  • В противном случае, ограничение сокращается до false.

18.2.5. Ограничения на исключения с проверкой

Формула ограничения вида ‹ВыражениеLambda →throws T› сокращается следующим образом:

  • Если T не является типом функционального интерфейса (§9.8), ограничение сводится к false.

  • В противном случае, тип целевой функции для лямбда-выражения определяется, как указано в §15.27.3. Если не найден допустимый тип функции, ограничение сводится к false.

  • В противном случае, если лямбда-выражение имеет неявный тип, и один или несколько типов параметров типа функции не являются правильными типами, ограничение сводится к false.

    Это условие никогда не возникает на практике из-за подстановки, применяемой к целевому типу в §18.5.2.2.

  • В противном случае, если тип возвращаемого значения функции не является void и не является правильным типом, ограничение сводится к false.

    Это условие никогда не возникает на практике из-за подстановки, применяемой к целевому типу в §18.5.2.2.

  • В противном случае, пусть E1, ..., En будут типами в части throws типа функции, которые не являются правильными типами. Если лямбда-выражение имеет неявный тип, пусть его типы параметров будут типами параметров функции. Если тело лямбда-выражения является поливыражением или блоком, содержащим полирезультатное выражение, пусть целевой тип возвращаемого значения будет типом возвращаемого значения функции. Пусть X1, ..., Xm — типы исключений с проверкой, которые может генерировать тело лямбда-выражения (§11.2). Тогда существует два случая:

    • Если n = 0 (часть throws типа функции состоит только из правильных типов), то если существует некоторый i (1 ≤ i ≤ m), такой что Xi не является подтипом какого-либо правильного типа в части throws, ограничение сводится к false; в противном случае ограничение сводится к true.

    • Если n > 0, ограничение сводится к набору ограничений на подтипизацию: для всех i (1 ≤ i ≤ m), если Xi не является подтипом какого-либо правильного типа в части throws, тогда ограничения включают для всех j (1 ≤ j ≤ n), ‹Xi <: Ej›. Кроме того, для всех j (1 ≤ j ≤ n), ограничение сводится к привязке throws Ej.

Формула ограничения вида ‹СсылкаНаМетод →throws T› сокращается следующим образом:

  • Если T не является типом функционального интерфейса, или если T является типом функционального интерфейса, но не имеет типа функции (§9.9), ограничение сводится к false.

  • В противном случае, пусть целевой тип функции для выражения ссылки на метод будет типом функции T. Если ссылка на метод является неточной (§15.13.1) и один или несколько типов параметров типа функции не являются правильными типами, ограничение сводится к false.

  • В противном случае, если ссылка на метод является неточной и результат функции не является void и не является правильным типом, ограничение сводится к false.

  • В противном случае, пусть E1, ..., En будут типами в части throws типа функции, которые не являются правильными типами. Пусть X1, ..., Xm — исключения с проверкой в части throws типа вызова для компиляционной декларации ссылки на метод (§15.13.2) (как полученные из типов параметров функции и типа возвращаемого значения). Тогда существует два случая:

    • Если n = 0 (часть throws типа функции состоит только из правильных типов), то если существует некоторый i (1 ≤ i ≤ m), такой что Xi не является подтипом какого-либо правильного типа в части throws, ограничение сводится к false; в противном случае ограничение сводится к true.

    • Если n > 0, ограничение сводится к набору ограничений на подтипизацию: для всех i (1 ≤ i ≤ m), если Xi не является подтипом какого-либо правильного типа в части throws, тогда ограничения включают для всех j (1 ≤ j ≤ n), ‹Xi <: Ej›. Кроме того, для всех j (1 ≤ j ≤ n), ограничение сводится к привязке throws Ej.

Ограничения на исключения с проверкой обрабатываются отдельно от ограничений на типы возвращаемых значений, так как совместимость типов возвращаемых значений влияет на применимость методов (§18.5.1), в то время как исключения влияют только на тип вызова после завершения разрешения перегрузки (§18.5.2). Это можно упростить, включив совместимость исключений в определение совместимости лямбда-выражений (§15.27.3), но это приведет к возможно неожиданным случаям, в которых исключения, которые могут генерироваться телом явно типизированного лямбда-выражения, меняют разрешение перегрузки.

Исключения, генерируемые телом лямбда-выражения, не могут быть определены до тех пор, пока (i) типы параметров лямбда-выражения не будут известны, и (ii) целевой тип выражений результата в теле не будет известен. (Второе требование предназначено для учета вызовов обобщенных методов, в которых, например, один и тот же параметр типа появляется в типе возвращаемого значения и в части throws). Поэтому нам требуется оба этих условия, полученные из целевого типа T, быть правильными типами.

Следствием является то, что лямбда-выражения, возвращаемые из других лямбда-выражений, не могут генерировать ограничения от своих сбрасываемых исключений. Эти ограничения могут генерироваться только от лямбда-выражений верхнего уровня.

Обратите внимание, что обработка случая, когда в части throws типа функции присутствует более чем одна переменная вывода, не сохраняет полноту. Любая переменная может сама по себе удовлетворить ограничение, что каждое исключение с проверкой должно быть объявлено, но мы не можем быть уверены, какое из них имелось в виду. Поэтому, для предсказуемости, мы ограничиваем их оба.

18.3. Включение

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

Включение может произойти в двух сценариях. Один сценарий — множество границ содержит дополнительные пары границ; это подразумевает новые формулы ограничений, как указано в §18.3.1. Другой сценарий — множество границ содержит границу, включающую преобразование захвата; это подразумевает новые границы и может подразумевать новые формулы ограничений, как указано в §18.3.2. В обоих сценариях любые новые формулы ограничений сводятся, и любые новые границы добавляются в множество границ. Это может привести к дальнейшему включению; в конечном итоге, множество достигнет неподвижной точки, и больше границ выведено быть не может.

Если включение множества границ достигло неподвижной точки, и множество не содержит границу false, то множество границ обладает следующими свойствами:

  • Для каждой комбинации надлежащей нижней границы L и надлежащей верхней границы U переменной вывода, L <: U.

  • Если каждая переменная вывода, упомянутая в границе, имеет экземпляр, то граница удовлетворяется соответствующей заменой.

  • При зависимости α = β каждая граница α соответствует границе β, и наоборот.

  • При зависимости α <: β, каждая нижняя граница α является нижней границей β, а каждая верхняя граница β является верхней границей α.

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

  • Новые переменные захвата не генерируются при сокращении ограничений подтипирования (§18.2.3).

  • Не рассматриваются расширяющиеся пути наследования.

Данное спецификация не гарантирует этих свойств в настоящий момент (она неточна в отношении обработки подстановок при сокращении ограничений подтипирования и не обнаруживает расширяющиеся пути наследования), но может сделать это в будущей версии. (Это не новая проблема: алгоритм подтипирования Java также может иметь риски не завершения).

18.3.1. Дополнительные пары границ

(В этой секции S и T — переменные вывода или типы, а U — надлежащий тип. Для краткости граница вида α = T также может соответствовать границе вида T = α.)

Когда множество границ содержит пару границ, соответствующую одному из следующих правил, подразумевается новая формула ограничения:

  • α = S и α = T подразумевают ‹S = T›

  • α = S и α <: T подразумевают ‹S <: T›

  • α = S и T <: α подразумевают ‹T <: S›

  • S <: α и α <: T подразумевают ‹S <: T›

  • α = U и S = T подразумевают ‹S[α:=U] = T[α:=U]›

  • α = U и S <: T подразумевают ‹S[α:=U] <: T[α:=U]›

Когда множество границ содержит пару границ α <: S и α <: T, и существует надтип S вида G<S1, ..., Sn> и надтип T вида G<T1, ..., Tn> (для некоторого обобщенного класса или интерфейса G), то для всех i (1 ≤ i ≤ n), если Si и Ti являются типами (не подстановками), то формула ограничения ‹Si = Ti› подразумевается.

18.3.2. Границы, включающие преобразование захвата

Когда множество границ содержит границу вида G<α1, ..., αn> = capture(G<A1, ..., An>), подразумеваются новые границы и могут быть выведены новые формулы ограничений, как следует.

Пусть P1, ..., Pn представляют параметры типа G, а B1, ..., Bn – границы этих параметров типа. Пусть θ представляет подстановку [P1:=α1, ..., Pn:=αn]. Пусть R – тип, который не является переменной вывода (но не обязательно является правильным типом).

Множество границ для α1, ..., αn подразумевается, сгенерированное из объявленных границ P1, ..., Pn, как указано в §18.1.3.

Кроме того, для всех i (1 ≤ i ≤ n):

  • Если Ai не является джойнд-операндом, то граница αi = Ai подразумевается.

  • Если Ai является джойнд-операндом вида ?:

    • αi = R подразумевает границу false

    • αi <: R подразумевает формулу ограничения ‹Bi θ <: R›

    • R <: αi подразумевает границу false

  • Если Ai является джойнд-операндом вида ? extends T:

    • αi = R подразумевает границу false

    • Если Bi – Object, то αi <: R подразумевает формулу ограничения ‹T <: R›

    • Если T – Object, то αi <: R подразумевает формулу ограничения ‹Bi θ <: R›

    • R <: αi подразумевает границу false

  • Если Ai является джойнд-операндом вида ? super T:

    • αi = R подразумевает границу false

    • αi <: R подразумевает формулу ограничения ‹Bi θ <: R›

    • R <: αi подразумевает формулу ограничения ‹R <: T›

18.4. Разрешение

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

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

  • Учитывая границу одного из следующих видов, где T – либо переменная вывода β, либо тип, содержащий β:

    • α = T

    • α <: T

    • T = α

    • T <: α

    Если α появляется в левой части другой границы вида G<..., α, ...> = capture(G<...>), то β зависит от разрешения α. В противном случае α зависит от разрешения β.

  • Переменная вывода α, появляющаяся в левой части границы вида G<..., α, ...> = capture(G<...>) зависит от разрешения каждой другой переменной вывода, упомянутой в этой границе (с обеих сторон знака =).

  • Переменная вывода α зависит от разрешения переменной вывода β, если существует переменная вывода γ такая, что α зависит от разрешения γ, а γ зависит от разрешения β.

  • Переменная вывода α зависит от разрешения от себя.

Учитывая множество переменных вывода для разрешения, пусть V будет объединением этого множества и всех переменных, от разрешения которых зависит по крайней мере одна переменная в этом множестве.

Если каждая переменная в V имеет подстановку, то разрешение проходит успешно и эта процедура завершается.

В противном случае пусть { α1, ..., αn } – непустое подмножество неинициализированных переменных в V такое, что (i) для всех i (1 ≤ i ≤ n), если αi зависит от разрешения переменной β, то либо β имеет подстановку, либо существует некоторое j такое, что β = αj; и (ii) не существует непустого собственного подмножества { α1, ..., αn } с этим свойством. Разрешение продолжается путем генерации подстановки для каждой из α1, ..., αn на основе границ в множестве границ:

END_OF_DOCUMENT_MARKER
  • Если связанное множество не содержит связи вида G<..., αi, ...> = capture(G<...>) для всех i (1 ≤ i ≤ n), то для каждого αi определяется кандидатская инстанциация Ti:

    • Если αi имеет одну или более собственных нижних границ, L1, ..., Lk, то Ti = lub(L1, ..., Lk) (§4.10.4).

    • В противном случае, если множество связей содержит throws αi, и каждая собственная верхняя граница αi является супертипом RuntimeException, то Ti = RuntimeException.

    • В противном случае, где αi имеет собственные верхние границы U1, ..., Uk, Ti = glb(U1, ..., Uk) (§5.1.10).

    Границы α1 = T1, ..., αn = Tn включаются в текущее множество связей.

    Если результат не содержит связь false, то результат становится новым множеством связей, и разрешение продолжается выбором новой группы переменных для инстанциации (при необходимости), как описано выше.

    В противном случае, результат содержит связь false, поэтому предпринимается вторая попытка инстанцировать { α1, ..., αn } выполнением следующего шага.

  • Если множество связей содержит связь вида G<..., αi, ...> = capture(G<...>) для некоторого i (1 ≤ i ≤ n), или;

    Если множество связей, полученное на предыдущем шаге, содержит связь false;

    то пусть Y1, ..., Yn — свежие переменные типа, границы которых задаются следующим образом:

    • Для всех i (1 ≤ i ≤ n), если αi имеет одну или более собственных нижних границ L1, ..., Lk, то пусть нижняя граница Yi будет lub(L1, ..., Lk); в противном случае, Yi не имеет нижней границы.

    • Для всех i (1 ≤ i ≤ n), где αi имеет верхние границы U1, ..., Uk, пусть верхняя граница Yi будет glb(U1 θ, ..., Uk θ), где θ — подстановка [α1:=Y1, ..., αn:=Yn].

    Если переменные типа Y1, ..., Yn не имеют корректных границ (то есть, нижняя граница не является подтипом верхней границы, или пересечение типов несовместимо), то разрешение терпит неудачу.

    В противном случае, для всех i (1 ≤ i ≤ n), все связи вида G<..., αi, ...> = capture(G<...>) удаляются из текущего множества связей, и связи α1 = Y1, ..., αn = Yn включаются.

    Если результат не содержит связь false, то результат становится новым множеством связей, и разрешение продолжается выбором новой группы переменных для инстанциации (при необходимости), как описано выше.

    В противном случае, результат содержит связь false, и разрешение терпит неудачу.

Первый метод инстанцирования переменной вывода вычисляет инстанциацию из границ этой переменной. Однако иногда сложные зависимости означают, что результат не попадает в пределы переменной. В этом случае выполняется другой метод инстанциации, аналогичный преобразованию захвата (§5.1.10): вводятся новые переменные типа со границами, выведенными из границ переменных вывода. Обратите внимание, что нижние границы этих «переменных захвата» вычисляются только для правильных типов: это важно, чтобы избежать попыток выполнения вычислений типизации на неинициализированных переменных типа.

18.5. Использование вывода

Используя описанные выше процессы вывода, следующие анализы выполняются во время компиляции.

18.5.1. Вывод применимости вызова

Для вызова метода, не предоставляющего явных аргументов типа, процесс определения применимости потенциально применимого дженерического метода m выполняется следующим образом:

  • Пусть P1, ..., Pp (p ≥ 1) — параметры типа m, пусть α1, ..., αp — переменные вывода, и пусть θ — подстановка [P1:=α1, ..., Pp:=αp].

  • Набор начальных ограничений B0 генерируется из объявленных ограничений P1, ..., Pp, как описано в §18.1.3.

  • Для всех i (1 ≤ i ≤ p), если Pi появляется в разделе throws для m, то ограничение throws αi подразумевается. Эти ограничения, если таковые имеются, включаются в B0 для получения нового набора ограничений B1.

  • Множество формул ограничений C генерируется следующим образом.

    Пусть F1, ..., Fn — типы формальных параметров m, а e1, ..., ek — выражения фактических аргументов вызова. Тогда:

    • Для проверки применимости по строгому вызову:

      Если k ≠ n, или если существует i (1 ≤ i ≤ n), такое что ei относится к применимости (§15.12.2.2) и либо (i) ei является автономным выражением примитивного типа, но Fi — ссылочный тип, или (ii) Fi — примитивный тип, но ei не является автономным выражением примитивного типа; тогда метод неприменим, и нет необходимости продолжать вывод.

      В противном случае C включает для всех i (1 ≤ i ≤ k), где ei относится к применимости, ‹ei → Fi θ›.

    • ...

Рассмотрим следующий вызов метода и присвоение:

List<Number> ln = Arrays.asList(1, 2.0);

Необходимо определить наиболее специфичный применимый метод для вызова, как описано в §15.12. Единственный потенциально применимый метод (§15.12.2.1) объявлен следующим образом:

public static <T> List<T> asList(T... a)

Тривиально (из-за его арности), этот метод неприменим ни по строгому вызову (§15.12.2.2), ни по свободному вызову (§15.12.2.3). Но поскольку других кандидатов нет, на третьем этапе проверяется применимость метода по вызову с переменным количеством аргументов.

Начальный набор ограничений B — тривиальное верхнее ограничение для одной переменной вывода α:

{ α <: Object }

Начальный набор формул ограничений следующий:

{ ‹1 → α›, ‹2.0 → α› }

Они сводятся к новому набору ограничений B1:

{ α <: Object, Integer <: α, Double <: α }

Затем, для проверки применимости метода, мы пытаемся разрешить эти ограничения. Мы добиваемся успеха, получив довольно сложное подстановку

α = Number & Comparable<? extends Number & Comparable<?>>

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

18.5.2. Вывод типа вызова

Для выражения вызова метода, не предоставляющего явных аргументов типа, и соответствующего наиболее специфичного применимого дженерического метода m, процесс вывода типа вызова (§15.12.2.6) выбранного метода может потребовать разрешения дополнительных ограничений, как для проверки совместимости с целевым типом, так и для подтверждения корректности выражений аргументов вызова метода.

Важно отметить, что для нахождения типа вызова метода участвуют несколько "раундов" вывода. Это необходимо, например, для того, чтобы позволить целевому типу влиять на тип вызова без возможности влиять на выбор применимого метода. Первый раунд (§18.5.1) создаёт набор ограничений и проверяет, существует ли разрешение, но не привязывается к этому разрешению. Последующие раунды уменьшают дополнительные ограничения до тех пор, пока заключительный шаг разрешения не определит "настоящий" тип выражения.

18.5.2.1. Совместимость вызова полиморфного метода

Если выражение вызова метода является поливыражением (§15.12), его совместимость с целевым типом T определяется следующим образом.

Если выражение вызова метода появляется в строгом контексте вызова, и T является примитивным типом, то выражение не совместимо с T.

В противном случае:

  • Пусть B2 — множество ограничений, полученное в результате сокращения, чтобы показать, что m применим в §18.5.1.

    (Хотя в §18.5.1 необходимо было продемонстрировать, что переменные вывода в B2 могут быть разрешены, результаты этих шагов разрешения не считаются частью B2.)

  • Пусть B3 — множество ограничений, полученное из B2 следующим образом.

    Пусть R — тип возвращаемого значения m, и пусть θ — подстановка [P1:=α1, ..., Pp:=αp], определенная в §18.5.1 для замены типов параметров m на переменные вывода, и пусть T — целевой тип вызова. Тогда:

    • Если для применения метода во время сокращения набора ограничений в §18.5.1 было необходимо неявное преобразование, то формула ограничения ‹|R| → T› сокращается и включается в B2.

    • В противном случае, если R θ является параметризованным типом, G<A1, ..., An>, и один из A1, ..., An является диким символом, то для свежих переменных вывода β1, ..., βn формула ограничения ‹G<β1, ..., βn> → T› сокращается и включается, вместе с ограничением G<β1, ..., βn> = capture(G<A1, ..., An>), в B2.

    • В противном случае, если R θ является переменной вывода α, и выполняется одно из следующих условий:

      • T — тип ссылки, но не дикий параметризованный тип, и либо (i) B2 содержит ограничение одного из видов α = S или S <: α, где S — дикий параметризованный тип, или (ii) B2 содержит два ограничения видов S1 <: α и S2 <: α, где S1 и S2 имеют надтипы, которые являются двумя различными параметризациями одного и того же обобщенного класса или интерфейса.

      • T — параметризация обобщенного класса или интерфейса, G, и B2 содержит ограничение одного из видов α = S или S <: α, где не существует типа вида G<...>, который является надтипом S, но необработанный тип |G<...>| является надтипом S.

      • T — примитивный тип, и один из примитивных оберток, упомянутых в §5.1.7, является экземпляром, верхним или нижним пределом для α в B2.

      тогда α разрешается в B2, и где захват полученного экземпляра α равен U, формула ограничения ‹U → T› сокращается и включается в B2.

    • В противном случае формула ограничения ‹R θ → T› сокращается и включается в B2.

  • Выражение вызова метода совместимо с T, если B3 не содержит ограничение false и разрешение всех переменных вывода в B3 успешно (§18.4).

Рассмотрим пример из предыдущего раздела:

List<Number> ln = Arrays.asList(1, 2.0);

Наиболее специфичный применимый метод был идентифицирован как:

public static <T> List<T> asList(T... a)

Для завершения проверки типа вызова метода необходимо определить, совместим ли он со своим целевым типом, List<Number>.

Множество ограничений, используемых для демонстрации применимости в предыдущем разделе, B2, было:

{ α <: Object, Integer <: α, Double <: α }

Новый набор формул ограничений выглядит следующим образом:

{ ‹List<α> → List<Number>› }

Это ограничение совместимости порождает ограничение равенства для α, которое включается в новый набор ограничений, B3:

{ α <: Object, Integer <: α, Double <: α, α = Number }

Эти ограничения тривиально разрешаются:

α = Number

Наконец, мы выполняем подстановку в декларированном типе возвращаемого значения asList, чтобы определить, что вызов метода имеет тип List<Number>; очевидно, это совместимо с целевым типом.

Эта стратегия вывода типов отличается от Java SE 7 Edition спецификации языка Java, которая бы подставила α на основе его нижних границ (даже не рассматривая целевой тип вызова), как мы делали в предыдущем разделе. Это привело бы к ошибке типа, поскольку полученный тип не является подтипом List<Number>.

В различных особых случаях, основываясь на ограничениях, присутствующих в B2, мы предвосхищаем разрешение переменной вывода, которая появляется как тип возвращаемого значения вызова. Это позволяет избежать нежелательных ситуаций, в которых обычное ограничение ‹R θ → T› не сохраняет полноту. К сожалению, возможно, что предвосхищенное разрешение не позволит использовать ограничения, которые будут выведены позже. Также возможно, что в некоторых случаях ограничения, которые будут выведены позже из аргументов вызова (например, неявные лямбда-выражения), привели бы к другому результату, если бы они были присутствовали в B2. Несмотря на эти ограничения, стратегия позволяет получить разумные результаты в типичных случаях использования и совместима со стратегией в Java SE 7 Edition спецификации языка Java.

18.5.2.2. Дополнительные ограничения аргументов

Тип вызова выбранного метода определяется после рассмотрения дополнительных ограничений, которые могут быть выведены из выражений аргументов выражения вызова метода, следующим образом:

  • Если выражение вызова метода является поливыражением, пусть B3 — множество ограничений, сгенерированное в §18.5.2.1 для демонстрации совместимости с фактическим целевым типом вызова метода.

    Если выражение вызова метода не является поливыражением, пусть B3 — то же самое множество ограничений, полученное в результате редукции, чтобы продемонстрировать, что m применима в §18.5.1.

    (Хотя в §18.5.1 и §18.5.2.1 необходимо было показать, что переменные вывода в множестве ограничений могут быть решены, созданные этими шагами решения не считаются частью B3.)

  • Множество формул ограничений, C, генерируется следующим образом.

    Пусть e1, ..., ek — фактические выражения аргументов выражения вызова метода.

    Если m применима посредством строгого или слабого вызова, пусть F1, ..., Fk — типы формальных параметров m; если m применима посредством вызова с переменным числом аргументов, пусть F1, ..., Fk — первые k типов параметров с переменным числом аргументов m (§15.12.2.4).

    Пусть θ — подстановка [P1:=α1, ..., Pp:=αp], определённая в §18.5.1 для замены типов параметров m переменными вывода.

    Затем, для всех i (1 ≤ i ≤ k):

    • Если ei не имеет отношения к применимости, C содержит ‹ei → Fi θ›.

    • Дополнительные ограничения могут быть включены в зависимости от формы ei:

      • Если ei — LambdaExpression, C содержит ‹LambdaExpression →throws Fi θ›, и в теле лямбда-выражения ищут дополнительные ограничения:

        • Для тела лямбда-выражения в виде блока поиск применяется рекурсивно к каждому выражению результата.

        • Для выражения создания экземпляра класса полиформа или поли-выражения вызова метода C содержит все формулы ограничений, которые появились бы в множестве C, сгенерированном по §18.5.2 при выводе типа вызова поливыражения.

        • Для скобочного выражения поиск применяется рекурсивно к содержаемому выражению.

        • Для условного выражения поиск применяется рекурсивно ко второму и третьему операндам.

        • Для лямбда-выражения поиск применяется рекурсивно к телу лямбда-выражения.

      • Если ei — MethodReference, C содержит ‹MethodReference →throws Fi θ›.

      • Если ei — выражение создания экземпляра класса полиформа или поли-выражение вызова метода, C содержит все формулы ограничений, которые появились бы в множестве C, сгенерированном по §18.5.2 при выводе типа вызова поливыражения.

      • Если ei — скобочное выражение, эти правила применяются рекурсивно к содержаемому выражению.

      • Если ei — условное выражение, эти правила применяются рекурсивно ко второму и третьему операндам.

  • Пока C не пусто, повторяется следующий процесс, начиная с множества ограничений B3 и накапливая новые ограничения в текущее множество ограничений, в конечном итоге создавая новое множество ограничений B4:

    1. Выбирается подмножество ограничений в C, удовлетворяющее свойству, что для каждого ограничения ни одна входная переменная не может влиять на выходную переменную другого ограничения в C. Термины входная переменная и выходная переменная определены ниже. Переменная вывода α может влиять на переменную вывода β, если α зависит от решения β (§18.4), или наоборот; или если существует третья переменная вывода γ такая, что α может влиять на γ и γ может влиять на β.

      Если это подмножество пусто, то существует цикл (или циклы) в графе зависимостей между ограничениями. В этом случае рассматриваются ограничения в C, участвующие в цикле (или циклах) зависимостей и не зависящие ни от каких ограничений за пределами цикла (или циклов). Выбирается одно ограничение из этих рассматриваемых ограничений, следующим образом:

      • Если какое-либо из рассматриваемых ограничений имеет вид ‹Выражение → T›, то выбранным ограничением является рассматриваемое ограничение такого вида, содержащее выражение слева (§3.5) от выражения каждого другого рассматриваемого ограничения такого вида.

      • Если ни одно из рассматриваемых ограничений не имеет вид ‹Выражение → T›, то выбранным ограничением является рассматриваемое ограничение, содержащее выражение слева от выражения каждого другого рассматриваемого ограничения.

    2. Выбранные ограничения удаляются из C.

    3. Входные переменные α1, ..., αm всех выбранных ограничений разрешаются.

    4. Где T1, ..., Tm — реализации α1, ..., αm, применяется подстановка [α1:=T1, ..., αm:=Tm] к каждому ограничению.

    5. Полученное ограничение(я) после подстановки сводится и включается в текущее множество ограничений.

  • Наконец, если B4 не содержит привязку false, переменные вывода в B4 разрешаются.

    Если разрешение успешно с инстанцированиями T1, ..., Tp для переменных вывода α1, ..., αp, пусть θ' будет заменой [P1:=T1, ..., Pp:=Tp]. Тогда:

    • Если для применимости метода требовалось неконтролируемое преобразование при уменьшении набора ограничений в §18.5.1, то типы параметров типа вызова m получаются путем применения θ' к типам параметров типа m, а тип возвращаемого значения и типы исключений типа вызова m задаются стиранием типа возвращаемого значения и типов исключений типа m.

    • Если неконтролируемое преобразование не требовалось для применимости метода, то тип вызова m получается путем применения θ' к типу m.

    Если B4 содержит привязку false, или если разрешение завершается неудачей, то возникает ошибка времени компиляции.

Процесс уменьшения дополнительных ограничений аргументов может потребовать тщательного упорядочивания формул ограничений вида ‹Expression → T›, ‹LambdaExpression →throws T› и ‹MethodReference →throws T›. Чтобы упростить этот порядок, следующие образом определяются входные переменные этих ограничений:

  • Для ‹LambdaExpression → T›:

    • Если T является переменной вывода, то это (единственная) входная переменная.

    • Если T является типом функционального интерфейса, и из T можно вывести тип функции (§15.27.3), то входные переменные включают (i) если лямбда-выражение неявно типизировано, переменные вывода, упоминаемые типами параметров типа функции; и (ii) если тип возвращаемого значения типа функции, R, не является void, то для каждого выражения результата e в теле лямбды (или для самого тела, если оно является выражением), входные переменные ‹e → R›.

    • В противном случае входные переменные отсутствуют.

  • Для ‹LambdaExpression →throws T›:

    • Если T является переменной вывода, то это (единственная) входная переменная.

    • Если T является типом функционального интерфейса, и можно вывести тип функции, как описано в §15.27.3, то входные переменные включают (i) если лямбда-выражение неявно типизировано, переменные вывода, упоминаемые типами параметров типа функции; и (ii) переменные вывода, упоминаемые типом возвращаемого значения типа функции.

    • В противном случае входные переменные отсутствуют.

  • Для ‹MethodReference → T›:

    • Если T является переменной вывода, то это (единственная) входная переменная.

    • Если T является типом функционального интерфейса с типом функции, и если ссылка на метод является неточной (§15.13.1), входными переменными являются переменные вывода, упоминаемые типами параметров типа функции.

    • В противном случае входные переменные отсутствуют.

  • Для ‹MethodReference →throws T›:

    • Если T является переменной вывода, то это (единственная) входная переменная.

    • Если T является типом функционального интерфейса с типом функции, и если ссылка на метод является неточной (§15.13.1), входными переменными являются переменные вывода, упоминаемые типами параметров типа функции, и типом возвращаемого значения типа функции.

    • В противном случае входные переменные отсутствуют.

  • Для ‹Expression → T›, если Expression является выражением в скобках:

    Где содержащееся выражение Expression является Expression', входными переменными являются входные переменные ‹Expression' → T›.

  • Для ‹ConditionalExpression → T›:

    Где условное выражение имеет форму e1 ? e2 : e3, входными переменными являются входные переменные ‹e2 → T› и ‹e3 → T›.

  • Для всех других формул ограничений входные переменные отсутствуют.

Выходными переменными этих ограничений являются все переменные вывода, упоминаемые типом в правой части ограничения, T, которые не являются входными переменными.

18.5.3. Вывод параметризации функциональных интерфейсов

Если лямбда-выражение со явными типами параметров P1, ..., Pn ориентировано на тип функционального интерфейса F<A1, ..., Am> с по меньшей мере одним аргументом типа-подстановкой, то параметризация F может быть выведена как базовый целевой тип лямбда-выражения следующим образом.

Пусть Q1, ..., Qk — типы параметров типа функции типа F<α1, ..., αm>, где α1, ..., αm — новые переменные вывода.

Если n ≠ k, то допустимая параметризация не существует. В противном случае формируется набор формул ограничений, включающий для всех i (1 ≤ i ≤ n), ‹Pi = Qi›. Этот набор формул ограничений сводится к формированию набора ограничений B.

Если B содержит ограничение false, то допустимая параметризация не существует. В противном случае новая параметризация типа функционального интерфейса F<A'1, ..., A'm> создается следующим образом, для 1 ≤ i ≤ m:

  • Если B содержит инстанциацию (§18.1.3) для αi, T, то A'i = T.

  • В противном случае, A'i = Ai.

Если F<A'1, ..., A'm> не является корректным типом (то есть, аргументы типа не находятся в пределах своих границ), или если F<A'1, ..., A'm> не является подтипом F<A1, ..., Am>, то допустимая параметризация не существует. В противном случае, выведенная параметризация либо F<A'1, ..., A'm>, если все аргументы типа являются типами, либо непараметризованная параметризация (§9.9) F<A'1, ..., A'm>, если один или несколько аргументов типа всё ещё являются подстановками.

Для определения типа функции с параметрами типа-подстановки функционального интерфейса, нам нужно «инстанцировать» аргументы типа-подстановки конкретными типами. «По умолчанию» подходы заключается в простом замене подстановок на их границы, как описано в §9.8, но это приводит к ложным ошибкам в случаях, когда лямбда-выражение имеет явные типы параметров, которые не соответствуют границам подстановок. Например:

Predicate<? super Integer> p = (Number n) -> n.equals(23);

Лямбда-выражение является Predicate<Number>, который является подтипом Predicate<? super Integer>, но не Predicate<Integer>. Анализ в этом разделе используется для вывода, что Number является подходящим выбором для аргумента типа Predicate.

Тем не менее, анализ здесь, хотя и описан в терминах общего вывода типов, преднамеренно довольно прост. Единственные ограничения — это ограничения равенства, что означает, что сокращение сводится к простому сопоставлению шаблонов. Более мощная стратегия также могла бы вывести ограничения из тела лямбда-выражения. Но, учитывая возможные взаимодействия с выводом для окружающих и/или вложенных вызовов генерических методов, это ввело бы большое количество дополнительной сложности.

18.5.4. Вывод более специфичного метода

При проверке того, что один применимый метод является более специфичным, чем другой (§15.12.2.5), где второй метод является генерическим, необходимо проверить, можно ли вывести некоторую инстанциацию параметров типа второго метода, чтобы сделать первый метод более специфичным, чем второй.

Пусть m1 — первый метод, а m2 — второй метод. Где у m2 есть параметры типа P1, ..., Pp, пусть α1, ..., αp — переменные вывода, а θ — подстановка [P1:=α1, ..., Pp:=αp].

Пусть e1, ..., ek — выражения аргументов соответствующего вызова. Тогда:

  • Если m1 и m2 применимы по строгому или свободному вызову (§15.12.2.2, §15.12.2.3), то пусть S1, ..., Sk — формальные типы параметров m1, а T1, ..., Tk — результат применения θ к формальным типам параметров m2.

  • Если m1 и m2 применимы по вызову с переменным числом аргументов (§15.12.2.4), то пусть S1, ..., Sk — первые k типов параметров с переменным числом аргументов m1, а T1, ..., Tk — результат применения θ к первым k типам параметров с переменным числом аргументов m2.

Обратите внимание, что к S1, ..., Sk не применяется никакая подстановка; даже если m1 является генерическим, параметры типа m1 рассматриваются как переменные типа, а не переменные вывода.

Процесс определения, является ли m1 более специфичным, чем m2, следующий:

  • Сначала генерируется начальный набор ограничений, B, из объявленных границ P1, ..., Pp, как указано в §18.1.3.

  • Второй, для всех i (1 ≤ i ≤ k), генерируется набор формул ограничений или границ.

    Если Ti является надлежащим типом, результатом является true, если Si более специфичен, чем Ti для ei (§15.12.2.5), в противном случае false. (Обратите внимание, что Si всегда является надлежащим типом.)

    В противном случае, если Si и Ti не являются типами функционального интерфейса, генерируется формула ограничения ‹Si <: Ti›.

    В противном случае, если интерфейс Si является суперинтерфейсом или подинтерфейсом интерфейса Ti (или, если Si или Ti является пересекающимся типом, какой-либо интерфейс Si является суперинтерфейсом или подинтерфейсом какого-либо интерфейса Ti), генерируется формула ограничения ‹Si <: Ti›.

    В противном случае, пусть MTS будет функциональным типом захвата Si, пусть MTS' будет функциональным типом Si (без захвата), а MTT будет функциональным типом Ti. Если MTS и MTT имеют разное количество формальных параметров или параметров типа, или если MTS и MTS' не имеют одних и тех же параметров типа (§8.4.4), результатом является false. В противном случае следующие формулы ограничений или границы генерируются из параметров типа, типов формальных параметров и типов возвращаемых значений MTS и MTT:

    • Пусть A1, ..., An будут параметрами типа MTS, а B1, ..., Bn будут параметрами типа MTT.

      Пусть θ' будет подстановкой [B1:=A1, ..., Bn:=An]. Затем, для всех j (1 ≤ j ≤ n):

      • Если граница Aj упоминает одну из A1, ..., An, а граница Bj не является надлежащим типом, false.

      • В противном случае, где X - граница Aj, а Y - граница Bj, ‹X = Y θ'›.

      Если граница Aj упоминает одну из A1, ..., An, а граница Bj не является надлежащим типом, тогда создание ограничения равенства приведет к возможности того, что переменная вывода будет ограничена переменной типа вне области видимости. Поскольку инстанцирование переменной вывода переменной типа вне области видимости бессмысленно, мы предпочитаем избегать этой ситуации, немедленно отказываясь от нее, когда возникает такая возможность. Это упрощение не сохраняет полноту. (Этот же комментарий относится к обработке типов формальных параметров и типов возвращаемых значений ниже.)

    • Пусть U1, ..., Uk будут типами формальных параметров MTS, а V1, ..., Vk будут типами формальных параметров MTT. Затем, для всех j (1 ≤ j ≤ k):

      • Если Uj упоминает одну из A1, ..., An, а Vj не является надлежащим типом, false.

      • В противном случае, ‹Vj θ' <: Uj›, и, где U1', ..., Uk' являются типами формальных параметров MTS', а A1', ..., An' являются параметрами типа MTS', ‹Vj[B1:=A1', ..., Bn:=An'] = Uj'›

    • Пусть RS будет типом возвращаемого значения MTS, а RT будет типом возвращаемого значения MTT. Затем:

      • Если RS упоминает одну из A1, ..., An, а RT не является надлежащим типом, false.

      • В противном случае, если ei является явно типизированным лямбда-выражением:

        • Если RT является void, true.

        • В противном случае, если RS и RT являются типами функционального интерфейса, и ei имеет хотя бы одно выражение результата, то для каждого выражения результата в ei этот весь второй шаг повторяется для вывода ограничений, при которых RS более специфичен, чем RT θ' для данного выражения результата.

        • В противном случае, если RS является примитивным типом, а RT - нет, и ei имеет хотя бы одно выражение результата, и каждое выражение результата ei является самостоятельным выражением (§15.2) примитивного типа, true.

        • В противном случае, если RT является примитивным типом, а RS - нет, и ei имеет хотя бы одно выражение результата, и каждое выражение результата ei является либо самостоятельным выражением ссылочного типа, либо поли выражением, true.

        • В противном случае, ‹RS <: RT θ'›.

      • В противном случае, если ei является точной ссылкой на метод:

        • Если RT является void, true.

        • В противном случае, если RS является примитивным типом, а RT - нет, и объявление времени компиляции для ei имеет примитивный тип возвращаемого значения, true.

        • В противном случае, если RT является примитивным типом, а RS - нет, и объявление времени компиляции для ei имеет ссылочный тип возвращаемого значения, true.

        • В противном случае, ‹RS <: RT θ'›.

      • В противном случае, если ei является выражением в скобках, эти правила для ограничений, полученных из RS и RT, применяются рекурсивно для содержащегося выражения.

      • В противном случае, если ei является условным выражением, эти правила для ограничений, полученных из RS и RT, применяются рекурсивно для каждого из второго и третьего операндов.

      • В противном случае, false.

  • Во-третьих, если m2 применимо по вызову с переменной арностью и имеет k+1 параметр, то где Sk+1 — тип k+1 параметра с переменной арностью m1, а Tk+1 — результат применения θ к k+1 параметру с переменной арностью m2, генерируется ограничение ‹Sk+1 <: Tk+1›.

  • В-четвертых, сгенерированные границы и формулы ограничений сводятся и включаются в B для получения набора границ B'.

    Если B' не содержит ограничение false, и разрешение всех переменных вывода в B' успешно, то m1 более специфичен, чем m2.

    В противном случае, m1 не более специфичен, чем m2.

© Oracle and/or its affiliates. All rights reserved.
Licensed under the Oracle Technology Network License Agreement.

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API