Spec-Zone.ru › Java Language Specification 8

Глава 16. Определённая инициализация

Содержание

16.1. Определённая инициализация и выражения
16.1.1. Булевы константные выражения
16.1.2. Оператор логического И &&
16.1.3. Оператор логического ИЛИ ||
16.1.4. Оператор логического отрицания !
16.1.5. Условный оператор ? :
16.1.6. Условный оператор ? :
16.1.7. Другие выражения типа boolean
16.1.8. Выражения присваивания
16.1.9. Операторы ++ и --
16.1.10. Другие выражения
16.2. Определённая инициализация и операторы
16.2.1. Пустые операторы
16.2.2. Блоки
16.2.3. Операторы объявления локальных классов
16.2.4. Операторы объявления локальных переменных
16.2.5. Операторы с метками
16.2.6. Выражения-операторы
16.2.7. if Операторы
16.2.8. assert Операторы
16.2.9. switch Операторы
16.2.10. while Операторы
16.2.11. do Операторы
16.2.12. for Операторы
16.2.12.1. Часть инициализации оператора for
16.2.12.2. Часть инкремента оператора for
16.2.13. break, continue, return и throw Операторы
16.2.14. synchronized Операторы
16.2.15. try Операторы
16.3. Определённая инициализация и параметры
16.4. Определённая инициализация и инициализаторы массивов
16.5. Определённая инициализация и константы перечислений
16.6. Определённая инициализация и анонимные классы
16.7. Определённая инициализация и типы членов
16.8. Определённая инициализация и статические инициализаторы
16.9. Определённая инициализация, конструкторы и инициализаторы экземпляров

Каждая локальная переменная (§14.4) и каждое пустое final поле (§4.12.4, §8.3.1.2) должно иметь определённо присвоенное значение при любом обращении к его значению.

Обращение к его значению состоит из простого имени переменной (или, для поля, простого имени поля, квалифицированного this) встречающегося где-либо в выражении, за исключением левого операнда оператора простого присваивания = (§15.26.1).

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

Аналогично, каждая пустая final переменная должна быть присвоена не более одного раза; она должна быть определённо неприсвоенной при присвоении ей значения.

Такое присвоение определено тогда и только тогда, когда простое имя переменной (или, для поля, его простое имя, квалифицированное this) находится в левой части оператора присваивания.

Для каждого присваивания пустой final переменной переменная должна быть определённо неприсвоенной до присвоения, иначе происходит ошибка компиляции.

Остальная часть этой главы посвящена точному объяснению слов "определённо присвоено до" и "определённо неприсвоено до".

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

Анализ учитывает структуру операторов и выражений; он также предоставляет специальное обращение к операторам выражений !, &&, || и ? :, а также к булевым константным выражениям.

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

Пример 16-1. Определённая инициализация учитывает структуру операторов и выражений

Компилятор Java распознаёт, что k определённо инициализировано перед своим использованием (как аргумент вызова метода) в коде:


{
    int k;
    if (v > 0 && (k = System.in.read()) >= 0)
        System.out.println(k);
}

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


v > 0 && (k = System.in.read()) >= 0

равно true, а значение может быть true только если выполняется присвоение k (более точно, вычисляется).

Аналогично, компилятор Java распознает, что в коде:


{
    int k;
    while (true) {
        k = n;
        if (k >= 5) break;
        n = 6;
    }
    System.out.println(k);
}

переменная k определённо инициализирована оператором while, потому что выражение условия true никогда не имеет значение false, поэтому только оператор break может привести к нормальному завершению оператора while, а k определённо инициализировано до оператора break.

С другой стороны, код:


{
    int k;
    while (n < 4) {
        k = n;
        if (k >= 5) break;
        n = 6;
    }
    System.out.println(k);  /* k is not "definitely assigned"
                               before this statement */
}

должен быть отвергнут компилятором Java, поскольку в данном случае оператор while не гарантируется для выполнения его тела с точки зрения правил определённой инициализации.


Пример 16-2. Определённая инициализация не учитывает значения выражений

Компилятор Java должен генерировать ошибку компиляции для кода:


{
    int k;
    int n = 5;
    if (n > 2)
        k = 3;
    System.out.println(k);  /* k is not "definitely assigned"
                               before this statement */
}

даже если значение n известно на этапе компиляции, и, в принципе, на этапе компиляции можно знать, что присвоение k всегда будет выполнено (более точно, вычислено). Компилятор Java должен работать в соответствии с правилами, изложенными в данном разделе. Правила распознают только константные выражения; в данном примере выражение n > 2 не является константным выражением в соответствии с §15.28.

В качестве другого примера компилятор Java примет код:


void flow(boolean flag) {
    int k;
    if (flag)
        k = 3;
    else
        k = 4;
    System.out.println(k);
}

с точки зрения определённой инициализации k, так как правила, изложенные в этом разделе, позволяют ему сказать, что k инициализируется независимо от того, является ли flag значением true или false. Но правила не принимают вариант:


void flow(boolean flag) {
    int k;
    if (flag)
        k = 3;
    if (!flag)
        k = 4;
    System.out.println(k);  /* k is not "definitely assigned"
                               before this statement */
}

и поэтому компиляция этой программы должна привести к ошибке компиляции.


Пример 16-3. Определённая неприсвоение

Компилятор Java примет код:


void unflow(boolean flag) {
    final int k;
    if (flag) {
        k = 3;
        System.out.println(k);
    }
    else {
        k = 4;
        System.out.println(k);
    }
}

с точки зрения определённой неприсвоенности k, так как правила, изложенные в этом разделе, позволяют ему сказать, что k инициализируется не более одного раза (действительно, ровно один раз), независимо от того, является ли flag значением true или false. Но правила не принимают вариант:


void unflow(boolean flag) {
    final int k;
    if (flag) {
        k = 3;
        System.out.println(k);
    }
    if (!flag) {
        k = 4;
        System.out.println(k);  /* k is not "definitely unassigned"
                                   before this statement */
    }
}

и поэтому компиляция этой программы должна привести к ошибке компиляции.


Для точного указания всех случаев определённой инициализации правила в этом разделе определяют несколько технических терминов:

  • является ли переменная определённо присвоенной до оператора или выражения;

  • является ли переменная определённо не присвоенной до оператора или выражения;

  • является ли переменная определённо присвоенной после оператора или выражения; и

  • является ли переменная определённо не присвоенной после оператора или выражения.

Для логических выражений последние два случая уточняются до четырёх:

  • является ли переменная определённо присвоенной после выражения при истинности;

  • является ли переменная определённо не присвоенной после выражения при истинности;

  • является ли переменная определённо присвоенной после выражения при ложности; и

  • является ли переменная определённо не присвоенной после выражения при ложности.

Здесь, при истинности и при ложности относятся к значению выражения.

Например, локальная переменная k определённо получает значение после вычисления выражения:


a && ((k=m) > 5)

когда выражение является true, но не когда выражение является false (потому что если a ложно, то присвоение переменной k не обязательно выполняется (точнее, вычисляется)).

Фраза "V определённо присвоена после X" (где V — локальная переменная, а X — оператор или выражение) означает "V определённо присвоена после X, если X завершается нормально". Если X завершается аномально, присвоение не обязательно произошло, и эти правила учитывают это.

Необычным следствием этого определения является то, что "V определённо присвоена после break;" всегда истинно! Поскольку оператор break никогда не завершается нормально, то путём вакуумного истинности утверждается, что V получила значение, если оператор break завершается нормально.

Утверждение "V определённо не присвоена после X" (где V — переменная, а X — оператор или выражение) означает "V определённо не присвоена после X, если X завершается нормально".

Ещё более необычным следствием этого определения является то, что "V определённо не присвоена после break;" всегда истинно! Поскольку оператор break никогда не завершается нормально, то путём вакуумного истинности утверждается, что V не получила значение, если оператор break завершается нормально. (В этой связи, также вакуумно истинно, что луна сделана из зелёного сыра, если оператор break завершается нормально.)

В целом, есть четыре возможности для переменной V после выполнения оператора или выражения:

  • V определённо присвоена и не определённо не присвоена.

    (Правила анализа потока доказывают, что произошло присвоение V.)

  • V определённо не присвоена и не определённо присвоена.

    (Правила анализа потока доказывают, что присвоение V не произошло.)

  • V не определённо присвоена и не определённо не присвоена.

    (Правила не могут доказать, произошло ли присвоение V или нет.)

  • V определённо присвоена и определённо не присвоена.

    (Оператор или выражение не могут завершиться нормально.)

Для сокращения правил используется обычное сокращение «iff», означающее «если и только если». Мы также используем соглашение об сокращении: если правило содержит одно или несколько вхождений "[не]присвоена", то оно представляет собой два правила, одно с заменой всех вхождений "[не]присвоена" на «определённо присвоена», а другое с заменой на «определённо не присвоена».

Например:

  • V [не]присвоена после пустого оператора iff она [не]присвоена до пустого оператора.

должно пониматься как два правила:

  • V определённо присвоена после пустого оператора iff она определённо присвоена до пустого оператора.

  • V определённо не присвоена после пустого оператора iff она определённо не присвоена до пустого оператора.

На протяжении всей остальной части главы, если не указано иное, мы будем использовать V для обозначения локальной переменной или пустого final поля, которое находится в области видимости (§6.3). Аналогично, мы будем использовать a, b, c и e для обозначения выражений, а S и T для обозначения операторов. Мы будем использовать фразу «a — это V», чтобы означать, что a является либо простым именем переменной V, либо простым именем V с квалификатором this (без учёта скобок). Мы будем использовать фразу «a — это не V», чтобы обозначать отрицание фразы «a — это V».

Анализ определённого неприсвоения для операторов цикла поднимает особую проблему. Рассмотрим оператор while (e) S. Для определения того, является ли V определённо не присвоенной в каком-либо подвыражении e, нам необходимо определить, является ли V определённо не присвоенной до e. Можно предположить по аналогии с правилом определённого присвоения (§16.2.10), что V определённо не присвоена до e тогда и только тогда, когда она определённо не присвоена до оператора while. Однако, такое правило не подходит для наших целей. Если e имеет значение true, оператор S будет выполнен. Позже, если V будет присвоена оператором S, то в последующих итерациях V будет уже присвоена, когда будет вычисляться e. По предложенному правилу, V можно было бы присваивать несколько раз, чего мы стремимся избежать, введя эти правила.

Пересмотренное правило будет: "V определённо не присвоена до e тогда и только тогда, когда она определённо не присвоена до оператора while и определённо не присвоена после S". Однако, когда мы формулируем правило для S, мы получаем: "V определённо не присвоена до S тогда и только тогда, когда она определённо не присвоена после e при истинности". Это приводит к цикличности. По сути, V определённо не присвоена до условия цикла e только если она не присвоена после всего цикла!

Мы прерываем этот порочный круг, используя гипотетический анализ условия и тела цикла. Например, если мы предположим, что V определённо не присвоена до e (независимо от того, действительно ли V определённо не присвоена до e), и затем сможем доказать, что V была определённо не присвоена после e, то мы будем знать, что e не присваивает V. Это выражается более формально как:

При условии, что V определённо не присвоена до e, V определённо не присвоена после e.

Вариации на описанном анализе используются для определения обоснованных правил определённого неприсвоения для всех операторов цикла в языке программирования Java.

16.1. Определённое присваивание и выражения

16.1.1. Логические константные выражения

  • V является [не]присвоенным после любого константного выражения (§15.28), значение которого равно true, когда ложно.

  • V является [не]присвоенным после любого константного выражения, значение которого равно false, когда истинно.

  • V является [не]присвоенным после любого константного выражения, значение которого равно true, когда истинно, если и только если V было [не]присвоено до константного выражения.

  • V является [не]присвоенным после любого константного выражения, значение которого равно false, когда ложно, если и только если V было [не]присвоено до константного выражения.

  • V является [не]присвоенным после логического константного выражения, имеющего булево значение e, если и только если V было [не]присвоено после e, когда истинно, и V было [не]присвоено после e, когда ложно.

    Это эквивалентно утверждению, что V является [не]присвоенным после e, если и только если V было [не]присвоено до e.

Поскольку константное выражение, значение которого равно true, никогда не имеет значение false, и константное выражение, значение которого равно false, никогда не имеет значение true, первые два правила выполняются путём. Они полезны при анализе выражений, включающих операторы && (§16.1.2), || (§16.1.3), ! (§16.1.4) и ? : (§16.1.5).

16.1.2. Условный оператор &&

  • V является [не]присвоенным после a && b (§15.23), когда истинно, если и только если V является [не]присвоенным после b, когда истинно.

  • V является [не]присвоенным после a && b, когда ложно, если и только если V является [не]присвоенным после a, когда ложно, и V является [не]присвоенным после b, когда ложно.

  • V является [не]присвоенным до a, если и только если V является [не]присвоенным до a && b.

  • V является [не]присвоенным до b, если и только если V является [не]присвоенным после a, когда истинно.

  • V является [не]присвоенным после a && b, если и только если V является [не]присвоенным после a && b, когда истинно, и V является [не]присвоенным после a && b, когда ложно.

16.1.3. Условный оператор ||

  • V является [не]присвоенным после a || b (§15.24), когда истинно, если и только если V является [не]присвоенным после a, когда истинно, и V является [не]присвоенным после b, когда истинно.

  • V является [не]присвоенным после a || b, когда ложно, если и только если V является [не]присвоенным после b, когда ложно.

  • V является [не]присвоенным до a, если и только если V является [не]присвоенным до a || b.

  • V является [не]присвоенным до b, если и только если V является [не]присвоенным после a, когда ложно.

  • V является [не]присвоенным после a || b, если и только если V является [не]присвоенным после a || b, когда истинно, и V является [не]присвоенным после a || b, когда ложно.

16.1.4. Оператор логического отрицания !

  • V является [не]присвоенным после !a (§15.15.6), когда истинно, если и только если V является [не]присвоенным после a, когда ложно.

  • V является [не]присвоенным после !a, когда ложно, если и только если V является [не]присвоенным после a, когда истинно.

  • V является [не]присвоенным до a, если и только если V является [не]присвоенным до !a.

  • V является [не]присвоенным после !a, если и только если V является [не]присвоенным после !a, когда истинно, и V является [не]присвоенным после !a, когда ложно.

    Это эквивалентно утверждению, что V является [не]присвоенным после !a, если и только если V является [не]присвоенным после a.

16.1.5. Условный оператор ? :

Предположим, что b и c — это логические выражения.

  • V является [не]присвоенным после a ? b : c (§15.25), когда истинно, если и только если V является [не]присвоенным после b, когда истинно, и V является [не]присвоенным после c, когда истинно.

  • V является [не]присвоенным после a ? b : c, когда ложно, если и только если V является [не]присвоенным после b, когда ложно, и V является [не]присвоенным после c, когда ложно.

  • V является [не]присвоенным до a, если и только если V является [не]присвоенным до a ? b : c.

  • V является [не]присвоенным до b, если и только если V является [не]присвоенным после a, когда истинно.

  • V является [не]присвоенным до c, если и только если V является [не]присвоенным после a, когда ложно.

  • V является [не]присвоенным после a ? b : c, если и только если V является [не]присвоенным после a ? b : c, когда истинно, и V является [не]присвоенным после a ? b : c, когда ложно.

16.1.6. Условный оператор ? :

Предположим, что b и c являются выражениями, которые не имеют булевого типа.

  • V инициализирован(а) ([не]инициализирован(а)) после a ? b : c (§15.25) тогда и только тогда, когда V инициализирован(а) ([не]инициализирован(а)) после b и V инициализирован(а) ([не]инициализирован(а)) после c.

  • V инициализирован(а) ([не]инициализирован(а)) до a тогда и только тогда, когда V инициализирован(а) ([не]инициализирован(а)) до a ? b : c.

  • V инициализирован(а) ([не]инициализирован(а)) до b тогда и только тогда, когда V инициализирован(а) после a при истинном значении.

  • V инициализирован(а) ([не]инициализирован(а)) до c тогда и только тогда, когда V инициализирован(а) после a при ложном значении.

16.1.7. Другие выражения типа boolean

Предположим, что e является выражением типа boolean и не является булевой константной выражением, выражением логического отрицания !a, выражением условного логического умножения a && b, выражением условного логического сложения a || b или условным выражением a ? b : c.

  • V инициализирован(а) ([не]инициализирован(а)) после e при истинном значении тогда и только тогда, когда V инициализирован(а) ([не]инициализирован(а)) после e.

  • V инициализирован(а) ([не]инициализирован(а)) после e при ложном значении тогда и только тогда, когда V инициализирован(а) ([не]инициализирован(а)) после e.

16.1.8. Выражения присваивания

Рассмотрим выражение присваивания a = b, a += b, a -= b, a *= b, a /= b, a %= b, a <<= b, a >>= b, a >>>= b, a &= b, a |= b или a ^= b (§15.26).

  • V определённо инициализирован(а) после выражения присваивания тогда и только тогда, когда:

    • a — это V, или

    • V определённо инициализирован(а) после b.

  • V определённо не инициализирован(а) после выражения присваивания тогда и только тогда, когда a не равно V и V определённо не инициализирован(а) после b.

  • V инициализирован(а) ([не]инициализирован(а)) до a тогда и только тогда, когда V инициализирован(а) ([не]инициализирован(а)) до выражения присваивания.

  • V инициализирован(а) ([не]инициализирован(а)) до b тогда и только тогда, когда V инициализирован(а) после a.

Обратите внимание, что если a равно V, а V не определённо инициализирован(а) перед составным присваиванием, таким как a &= b, то неизбежно возникнет ошибка времени компиляции. Первое правило определённой инициализации содержит дизъюнкцию "a равно V", даже для выражений составного присваивания, а не только для простых присваиваний, так что V будет считаться определённо инициализированным в последующих частях кода. Включение дизъюнкции "a равно V" не влияет на бинарное решение о том, является ли программа приемлемой или приведёт к ошибке времени компиляции, но оно влияет на то, сколько разных точек в коде могут считаться ошибочными, а поэтому на практике может улучшить качество сообщений об ошибках. Аналогичное замечание относится к включению конъюнкции "a не равно V" в первом правиле определённой неинициализации, описанном выше.

16.1.9. Операторы ++ и --

  • V определённо инициализирован(а) после ++a (§15.15.1), --a (§15.15.2), a++ (§15.14.2), или a-- (§15.14.3) тогда и только тогда, когда либо a равно V, либо V определённо инициализирован(а) после выражения операнда.

  • V определённо не инициализирован(а) после ++a, --a, a++, или a-- тогда и только тогда, когда a не равно V и V определённо не инициализирован(а) после выражения операнда.

  • V инициализирован(а) ([не]инициализирован(а)) до a тогда и только тогда, когда V инициализирован(а) ([не]инициализирован(а)) до ++a, --a, a++, или a--.

16.1.10. Другие выражения

Если выражение не является булевым константным выражением, и не является прединкрементным выражением ++a, преддекрементным выражением --a, постдинкрементным выражением a++, постдекрементным выражением a--, логическим дополняющим выражением !a, условным-и выражением a && b, условным-или выражением a || b, условным выражением a ? b : c, или выражением присваивания, тогда применяются следующие правила:

  • Если выражение не имеет подвыражений, V считается [не]присвоенным после выражения тогда и только тогда, когда V [не]присвоен перед выражением.

    Этот случай применим к литералам, именам, this (как квалифицированным, так и неквалифицированным), неквалифицированным выражениям создания экземпляра класса без аргументов, инициализированным выражениям создания массива, инициализаторы которых не содержат выражений, неквалифицированным выражениям доступа к полям суперкласса, именованным вызовам методов без аргументов и неквалифицированным вызовам методов суперкласса без аргументов.

  • Если выражение имеет подвыражения, V считается [не]присвоенным после выражения тогда и только тогда, когда V [не]присвоен после его самого правого непосредственного подвыражения.

Существует тонкий момент в утверждении, что переменная V может быть определённо неприсвоенной после вызова метода. Само по себе, без ограничений, такое утверждение не всегда верно, потому что вызванный метод может выполнять присваивания. Но следует помнить, что в контексте языка программирования Java понятие определённого неприсваивания применяется только к пустым final переменным. Если V — пустая final локальная переменная, то только метод, к которому относится её объявление, может выполнить присваивание V. Если V — пустое final поле, то только конструктор или инициализатор класса, содержащий объявление для V, может выполнить присваивание V; ни один метод не может выполнить присваивание V. Наконец, явные вызовы конструкторов (§8.8.7.1) обрабатываются специально (§16.9); хотя они синтаксически похожи на операторы выражений, содержащие вызовы методов, они не являются операторами выражений, и поэтому правила этого раздела не применяются к явным вызовам конструкторов.

Для любого непосредственного подвыражения y выражения x, V считается [не]присвоенным перед y, если выполняется одно из следующих условий:

  • y является левым непосредственным подвыражением x и V считается [не]присвоенным перед x.

  • y является правым операндом бинарного оператора и V считается [не]присвоенным после левого операнда.

  • x — выражение доступа к массиву, y — подвыражение в скобках, и V считается [не]присвоенным после подвыражения перед скобками.

  • x — выражение первичного вызова метода, y — первое выражение аргумента в выражении вызова метода, и V считается [не]присвоенным после первичного выражения, вычисляющего целевой объект.

  • x — выражение вызова метода или выражение создания экземпляра класса; y — выражение аргумента, но не первое; и V считается [не]присвоенным после выражения аргумента слева от y.

  • x — квалифицированное выражение создания экземпляра класса, y — первое выражение аргумента в выражении создания экземпляра класса, и V считается [не]присвоенным после первичного выражения, вычисляющего квалифицирующий объект.

  • x — выражение создания массива; y — выражение размера, но не первое; и V считается [не]присвоенным после выражения размера слева от y.

  • x — выражение создания массива, инициализированное через инициализатор массива; y — инициализатор массива в x; и V считается [не]присвоенным после выражения размера слева от y.

16.2. Определённое присваивание и операторы

16.2.1. Операторы пустого тела

  • V [не]присвоено после оператора пустого тела (§14.6) тогда и только тогда, когда оно [не]присвоено перед оператором пустого тела.

16.2.2. Блоки

  • Поле-член V без значения является определённо присвоенным (и, кроме того, не определённо не присвоенным) перед блоком (§14.2), который является телом любого метода в области видимости V, и перед объявлением любого класса, объявленного в области видимости V.

  • Локальная переменная V определённо не присвоена (и, кроме того, не определённо присвоена) перед блоком, который является телом конструктора, метода, инициализатора экземпляра или статического инициализатора, объявляющего V.

  • Пусть C — класс, объявленный в области видимости V. Тогда V определённо присвоено перед блоком, который является телом любого конструктора, метода, инициализатора экземпляра или статического инициализатора, объявленного в C, тогда и только тогда, когда V определённо присвоено перед объявлением C.

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

  • V [не]присвоено после пустого блока, если V [не]присвоено перед пустым блоком.

  • V [не]присвоено после непустого блока, если V [не]присвоено после последнего оператора в блоке.

  • V [не]присвоено перед первым оператором блока, если V [не]присвоено перед блоком.

  • V [не]присвоено перед любым другим оператором S блока, если V [не]присвоено после оператора, непосредственно предшествующего S в блоке.

Мы говорим, что V определённо не присвоено повсюду в блоке B, если:

  • V определённо не присвоено перед B.

  • V определённо присвоено после e во всех операторах присваивания V = e, V += e, V -= e, V *= e, V /= e, V %= e, V <<= e, V >>= e, V >>>= e, V &= e, V |= e или V ^= e, который встречается в B.

  • V определённо присвоено перед каждым выражением ++V, --V, V++ или V--, который встречается в B.

Эти условия не интуитивны и требуют объяснения. Рассмотрим простое присваивание V = e. Если V определённо присвоено после e, то либо:

  • Присваивание происходит в мёртвом коде, и V вакуумно определённо присвоено. В этом случае присваивание фактически не произойдёт, и мы можем предположить, что V не присваивается выражением присваивания. Или:

  • V уже было присвоено предыдущим выражением до e. В этом случае текущее присваивание вызовет ошибку времени компиляции.

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

16.2.3. Операторы объявления локального класса

  • V [не]присвоено после оператора объявления локального класса (§14.3), если V [не]присвоено перед оператором объявления локального класса.

16.2.4. Операторы объявления локальных переменных

  • V [не]присвоено после оператора объявления локальной переменной, не содержащего инициализаторов переменных, если V [не]присвоено перед оператором объявления локальной переменной.

  • V определённо присвоено после оператора объявления локальной переменной, содержащего по крайней мере один инициализатор переменной, если либо V определённо присвоено после последнего инициализатора переменной в операторе объявления локальной переменной, либо последний инициализатор переменной в объявлении находится в деклараторе, объявляющем V.

  • V определённо не присвоено после оператора объявления локальной переменной, содержащего по крайней мере один инициализатор переменной, если V определённо не присвоено после последнего инициализатора переменной в операторе объявления локальной переменной и последний инициализатор переменной в объявлении не находится в деклараторе, объявляющем V.

  • V [не]присвоено перед первым инициализатором переменной в операторе объявления локальной переменной, если V [не]присвоено перед оператором объявления локальной переменной.

  • V определённо присвоено перед любым инициализатором переменной e, отличным от первого, в операторе объявления локальной переменной, если либо V определённо присвоено после инициализатора переменной слева от e, либо выражение инициализации слева от e находится в деклараторе, объявляющем V.

  • V определённо не присвоено перед любым инициализатором переменной e, отличным от первого, в операторе объявления локальной переменной, если V определённо не присвоено после инициализатора переменной слева от e и выражение инициализации слева от e не находится в деклараторе, объявляющем V.

16.2.5. Операторы с метками

  • V [не]присвоено после оператора с меткой L : S (где L — метка) (§14.7), если V [не]присвоено после S и V [не]присвоено перед каждым оператором break, который может завершить оператор с меткой L : S.

  • V [не]присвоено перед S, если V [не]присвоено перед L : S.

16.2.6. Операторы выражений

  • V является [не]инициализированным после оператора выражения e; (§14.8) тогда и только тогда, когда он является [не]инициализированным после e.

  • V является [не]инициализированным перед e тогда и только тогда, когда он является [не]инициализированным перед e;.

16.2.7. Операторы if

Следующие правила применяются к оператору if if (e) S (§14.9.1):

  • V является [не]инициализированным после if (e) S тогда и только тогда, когда V является [не]инициализированным после S и V является [не]инициализированным после e при ложном условии.

  • V является [не]инициализированным перед e тогда и только тогда, когда V является [не]инициализированным перед if (e) S.

  • V является [не]инициализированным перед S тогда и только тогда, когда V является [не]инициализированным после e при истинном условии.

Следующие правила применяются к оператору if-then-else if (e) S else T (§14.9.2):

  • V является [не]инициализированным после if (e) S else T тогда и только тогда, когда V является [не]инициализированным после S и V является [не]инициализированным после T.

  • V является [не]инициализированным перед e тогда и только тогда, когда V является [не]инициализированным перед if (e) S else T.

  • V является [не]инициализированным перед S тогда и только тогда, когда V является [не]инициализированным после e при истинном условии.

  • V является [не]инициализированным перед T тогда и только тогда, когда V является [не]инициализированным после e при ложном условии.

16.2.8. Операторы assert

Следующие правила применяются как к оператору assert assert e1, так и к оператору assert assert e1 : e2 (§14.10):

  • V является [не]инициализированным перед e1 тогда и только тогда, когда V является [не]инициализированным перед оператором assert.

  • V определённо инициализирован после оператора assert тогда и только тогда, когда V определённо инициализирован перед оператором assert.

  • V определённо не инициализирован после оператора assert тогда и только тогда, когда V определённо не инициализирован перед оператором assert и V определённо не инициализирован после e1 при истинном условии.

Следующее правило применяется к оператору assert e1 : e2 :

  • V является [не]инициализированным перед e2 тогда и только тогда, когда V является [не]инициализированным после e1 при ложном условии.

16.2.9. Операторы switch

  • V является [не]инициализированным после оператора switch switch (§14.11) тогда и только тогда, когда все из следующего истинны:

    • Есть метка case в блоке switch или V является [не]инициализированным после выражения switch.

    • Нет меток case в блоке switch, которые не начинаются с блока-группы операторов (т.е. нет меток case непосредственно перед "}" завершающим блок switch) или V является [не]инициализированным после выражения switch.

    • Блок switch не содержит блоков-групп операторов или V является [не]инициализированным после последнего оператора в последней группе блоков.

    • V является [не]инициализированным перед каждым оператором break, который может выйти из оператора switch switch.

  • V является [не]инициализированным перед выражением switch тогда и только тогда, когда V является [не]инициализированным перед оператором switch.

Если блок switch содержит хотя бы одну группу блоков, то также применяются следующие правила:

  • V является [не]инициализированным перед первым оператором первой группы блоков в блоке switch тогда и только тогда, когда V является [не]инициализированным после выражения switch.

  • V является [не]инициализированным перед первым оператором любой группы блоков, кроме первой, тогда и только тогда, когда V является [не]инициализированным после выражения switch и V является [не]инициализированным после предыдущего оператора.

16.2.10. Операторы while

  • V является [не]инициализированным после оператора while while (e) S (§14.12) тогда и только тогда, когда V является [не]инициализированным после e при ложном условии и V является [не]инициализированным перед каждым оператором break, для которого оператор while является точкой прерывания.

  • V определённо инициализирован перед e тогда и только тогда, когда V определённо инициализирован перед оператором while.

  • V определённо не инициализирован перед e тогда и только тогда, когда все из следующего истинны:

    • V определённо не инициализирован перед оператором while.

    • Предполагая, что V определённо не инициализирован перед e, V определённо не инициализирован после S.

    • Предполагая, что V определённо не инициализирован перед e, V определённо не инициализирован перед каждым оператором continue, для которого оператор while является точкой продолжения.

  • V является [не]инициализированным перед S тогда и только тогда, когда V является [не]инициализированным после e при истинном условии.

16.2.11. Операторы do

  • V является [не]инициализированным после оператора do do S while (e); (§14.13) тогда и только тогда, когда V является [не]инициализированным после e при ложном условии и V является [не]инициализированным перед каждым оператором break, для которого оператор do является точкой прерывания.

  • V определённо инициализирован перед S тогда и только тогда, когда V определённо инициализирован перед оператором do.

  • V определённо не инициализирован перед S тогда и только тогда, когда все из следующего истинны:

    • V определённо не инициализирован перед оператором do.

    • Предполагая, что V определённо не инициализирован перед S, V определённо не инициализирован после e при истинном условии.

  • V является [не]инициализированным перед e тогда и только тогда, когда V является [не]инициализированным после S и V является [не]инициализированным перед каждым оператором continue, для которого оператор do является точкой продолжения.

16.2.12. for Операторы

Приведённые правила охватывают базовый for оператор (§14.14.1). Поскольку расширенный for оператор (§14.14.2) определяется переводом в базовый for оператор, для него не требуется никаких специальных правил.

  • V [присваивается/не присваивается] после for оператора, если оба следующих условия истинны:

    • Либо выражение условия отсутствует, либо V [присваивается/не присваивается] после выражения условия, когда оно ложно.

    • V [присваивается/не присваивается] перед каждым break оператором, для которого for оператор является целевым оператором break.

  • V [присваивается/не присваивается] перед частью инициализации for оператора, если V [присваивается/не присваивается] перед for оператором.

  • V определённо присваивается перед частью условия for оператора, если V определённо присваивается после части инициализации for оператора.

  • V определённо не присваивается перед частью условия for оператора, если все следующие условия истинны:

    • V определённо не присваивается после части инициализации for оператора.

    • Предполагая, что V определённо не присваивается перед частью условия for оператора, V определённо не присваивается после содержащегося оператора.

    • Предполагая, что V определённо не присваивается перед содержащимся оператором, V определённо не присваивается перед каждым continue оператором, для которого for оператор является целевым оператором continue.

  • V [присваивается/не присваивается] перед содержащимся оператором, если истинно хотя бы одно из следующих условий:

    • Выражение условия присутствует и V [присваивается/не присваивается] после выражения условия, когда оно истинно.

    • Выражение условия отсутствует и V [присваивается/не присваивается] после части инициализации for оператора.

  • V [присваивается/не присваивается] перед частью инкрементации for оператора, если V [присваивается/не присваивается] после содержащегося оператора и V [присваивается/не присваивается] перед каждым continue оператором, для которого for оператор является целевым оператором continue.

16.2.12.1. Часть инициализации for оператора

  • Если часть инициализации for оператора — это оператор объявления локальной переменной, применяются правила §16.2.4.

  • В противном случае, если часть инициализации пустая, то V [присваивается/не присваивается] после части инициализации, если V [присваивается/не присваивается] перед частью инициализации.

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

    • V [присваивается/не присваивается] после части инициализации, если V [присваивается/не присваивается] после последнего оператора выражения в части инициализации.

    • V [присваивается/не присваивается] перед первым оператором выражения в части инициализации, если V [присваивается/не присваивается] перед частью инициализации.

    • V [присваивается/не присваивается] перед оператором выражения S, отличным от первого в части инициализации, если V [присваивается/не присваивается] после оператора выражения, непосредственно предшествующего S.

16.2.12.2. Часть инкрементации for оператора

  • Если часть инкрементации for оператора пустая, то V [присваивается/не присваивается] после части инкрементации, если V [присваивается/не присваивается] перед частью инкрементации.

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

    • V [присваивается/не присваивается] после части инкрементации, если V [присваивается/не присваивается] после последнего оператора выражения в части инкрементации.

    • V [присваивается/не присваивается] перед первым оператором выражения в части инкрементации, если V [присваивается/не присваивается] перед частью инкрементации.

    • V [присваивается/не присваивается] перед оператором выражения S, отличным от первого в части инкрементации, если V [присваивается/не присваивается] после оператора выражения, непосредственно предшествующего S.

16.2.13. break, continue, return и throw Операторы

  • По соглашению, мы говорим, что V [присваивается/не присваивается] после любого break, continue, return или throw оператора (§14.15, §14.16, §14.17, §14.18).

    Понятие, что переменная "[присваивается/не присваивается] после" оператора или выражения, на самом деле означает "переменная [присваивается/не присваивается] после того, как оператор или выражение завершились нормально". Поскольку break, continue, return или throw операторы никогда не завершаются нормально, это условие выполняется тривиально.

  • В return операторе с выражением e или в throw операторе с выражением e, V [присваивается/не присваивается] перед e, если V [присваивается/не присваивается] перед return или throw оператором.

16.2.14. synchronized Операторы

  • V [присваивается/не присваивается] после synchronized (e) S (§14.19), если V [присваивается/не присваивается] после S.

  • V [присваивается/не присваивается] перед e, если V [присваивается/не присваивается] перед оператором synchronized (e) S.

  • V [присваивается/не присваивается] перед S, если V [присваивается/не присваивается] после e.

16.2.15. try Операторы

Эти правила применяются к каждому оператору try (§14.20), независимо от того, есть ли у него блок finally:

  • V [присвоено/не присвоено] перед блоком try тогда и только тогда, когда V [присвоено/не присвоено] перед оператором try.

  • V определённо присвоено перед блоком catch тогда и только тогда, когда V определённо присвоено перед блоком try.

  • V определённо не присвоено перед блоком catch тогда и только тогда, когда выполняются все следующие условия:

    • V определённо не присвоено после блока try.

    • V определённо не присвоено перед каждым оператором return, принадлежащим блоку try.

    • V определённо не присвоено после e в каждом операторе вида throw e, принадлежащем блоку try.

    • V определённо не присвоено после каждого оператора assert, который встречается в блоке try.

    • V определённо не присвоено перед каждым оператором break, принадлежащим блоку try и в котором целевой оператор break содержит (или является) оператором try.

    • V определённо не присвоено перед каждым оператором continue, принадлежащим блоку try и в котором целевой оператор continue содержит оператор try.

Если оператор try не имеет блока finally, то это правило также применяется:

  • V [присвоено/не присвоено] после оператора try тогда и только тогда, когда V [присвоено/не присвоено] после блока try и V [присвоено/не присвоено] после каждого блока catch в операторе try.

Если оператор try имеет блок finally, то эти правила также применяются:

  • V определённо присвоено после оператора try тогда и только тогда, когда хотя бы одно из следующих условий истинно:

    • V определённо присвоено после блока try и V определённо присвоено после каждого блока catch в операторе try.

    • V определённо присвоено после блока finally.

    • V определённо не присвоено после оператора try тогда и только тогда, когда V определённо не присвоено после блока finally.

  • V определённо присвоено перед блоком finally тогда и только тогда, когда V определённо присвоено перед оператором try.

  • V определённо не присвоено перед блоком finally тогда и только тогда, когда выполняются все следующие условия:

    • V определённо не присвоено после блока try.

    • V определённо не присвоено перед каждым оператором return, принадлежащим блоку try.

    • V определённо не присвоено после e в каждом операторе вида throw e, принадлежащем блоку try.

    • V определённо не присвоено после каждого оператора assert, который встречается в блоке try.

    • V определённо не присвоено перед каждым оператором break, принадлежащим блоку try и в котором целевой оператор break содержит (или является) оператором try.

    • V определённо не присвоено перед каждым оператором continue, принадлежащим блоку try и в котором целевой оператор continue содержит оператор try.

    • V определённо не присвоено после каждого блока catch оператора try.

16.3. Определённое присваивание и параметры

  • Формальный параметр V метода или конструктора (§8.4.1, §8.8.1) определённо присвоен (и, следовательно, не определённо не присвоен) перед телом метода или конструктора.

  • Параметр исключения V блока catch (§14.20) определённо присвоен (и, следовательно, не определённо не присвоен) перед телом блока catch.

16.4. Определённое присваивание и инициализаторы массивов

  • V [присвоено/не присвоено] после пустого инициализатора массива (§10.6) тогда и только тогда, когда V [присвоено/не присвоено] перед пустым инициализатором массива.

  • V [присвоено/не присвоено] после непустого инициализатора массива тогда и только тогда, когда V [присвоено/не присвоено] после последнего инициализатора переменной в инициализаторе массива.

  • V [присвоено/не присвоено] перед первым инициализатором переменной в инициализаторе массива тогда и только тогда, когда V [присвоено/не присвоено] перед инициализатором массива.

  • V [присвоено/не присвоено] перед любым другим инициализатором переменной e инициализатора массива тогда и только тогда, когда V [присвоено/не присвоено] после инициализатора переменной слева от e в инициализаторе массива.

16.5. Определённое присваивание и константы перечислений

Правила, определяющие, когда переменная определённо присвоена или определённо не присвоена перед константой перечисления (§8.9.1), приведены в §16.8.

Это связано с тем, что константа перечисления по существу является static final полем (§8.3.1.1, §8.3.1.2), которая инициализируется выражением создания экземпляра класса (§15.9).

  • V определённо присвоено перед объявлением тела класса константы перечисления без аргументов, объявленной в области видимости V тогда и только тогда, когда V определённо присвоено перед константой перечисления.

  • V определённо присвоено перед объявлением тела класса константы перечисления с аргументами, объявленной в области видимости V, тогда и только тогда, когда V определённо присвоено после последнего выражения аргумента константы перечисления

Статус определённого присваивания/неопределённого присваивания любого фрагмента внутри тела класса константы перечисления регулируется стандартными правилами для классов.

  • V [присвоено/не присвоено] перед первым аргументом константы перечисления тогда и только тогда, когда оно [присвоено/не присвоено] перед константой перечисления.

  • V [присвоено/не присвоено] перед y (аргумент константы перечисления, но не первый) тогда и только тогда, когда V [присвоено/не присвоено] после аргумента слева от y.

16.6. Определённая инициализация и анонимные классы

  • V инициализирована перед объявлением анонимного класса (§15.9.5), который объявлен в области видимости V, если V инициализирована после выражения создания экземпляра класса, объявляющего анонимный класс.

Должно быть ясно, что если анонимный класс неявно определяется константой перечисления, применяются правила §16.5.

16.7. Определённая инициализация и типы членов

Пусть C — класс, а V — пустое final поле члена класса C. Тогда:

  • V инициализирована (и, более того, не неинициализирована) перед объявлением любого типа члена (§8.5, §9.5) класса C.

Пусть C — класс, объявленный в области видимости V. Тогда:

  • V инициализирована перед объявлением типа члена C, если V инициализирована перед объявлением C.

16.8. Определённая инициализация и статические инициализаторы

Пусть C — класс, объявленный в области видимости V. Тогда:

  • V инициализирована перед константой перечисления (§8.9.1) или статической инициализацией переменной (§8.3.2) класса C, если V инициализирована перед объявлением C.

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

Пусть C — класс, а V — пустое static final поле члена класса C, объявленное в C. Тогда:

  • V не инициализирована (и, более того, не инициализирована) перед самой левой константой перечисления, статической инициализацией (§8.7) или статической инициализацией переменной C.

  • V [не]инициализирована перед константой перечисления, статическим инициализатором или статической инициализацией переменной C, отличной от самой левой, если V [не]инициализирована после предыдущей константы перечисления, статического инициализатора или статической инициализации переменной C.

Пусть C — класс, а V — пустое static final поле члена класса C, объявленное в родительском классе C. Тогда:

  • V инициализирована (и, более того, не неинициализирована) перед каждой константой перечисления C.

  • V инициализирована (и, более того, не неинициализирована) перед блоком, являющимся телом статического инициализатора C.

  • V инициализирована (и, более того, не неинициализирована) перед каждой статической инициализацией переменной C.

END_OF_DOCUMENT_MARKER

16.9. Определённое присваивание, конструкторы и инициализаторы экземпляров

Пусть C — класс, объявленный в области видимости V. Тогда:

  • V определённо присвоен перед инициализатором переменной экземпляра (§8.3.2) класса C тогда и только тогда, когда V определённо присвоен перед объявлением C.

    Обратите внимание, что нет правил, позволяющих сделать вывод, что V определённо не присвоен перед инициализатором переменной экземпляра. Мы можем неофициально заключить, что V не определённо не присвоен перед любым инициализатором переменной экземпляра класса C, но нет необходимости в явном указании такого правила.

Пусть C — класс, а V — пустое final не-static поле члена класса C, объявленное в C. Тогда:

  • V определённо не присвоен (и, более того, не определённо присвоен) перед левым инициализатором экземпляра (§8.6) или инициализатором переменной экземпляра класса C.

  • V [не] присвоен перед инициализатором экземпляра или инициализатором переменной экземпляра класса C, отличным от левого, если и только если V [не] присвоен после предыдущего инициализатора экземпляра или инициализатора переменной экземпляра класса C.

Следующие правила применяются внутри конструкторов (§8.8.7) класса C:

  • V определённо присвоен (и, более того, не определённо не присвоен) после вызова альтернативного конструктора (§8.8.7.1).

  • V определённо не присвоен (и, более того, не определённо присвоен) перед явным или неявным вызовом конструктора суперкласса (§8.8.7.1).

  • Если у C нет инициализаторов экземпляров или инициализаторов переменных экземпляров, то V не определённо присвоен (и, более того, определённо не присвоен) после явного или неявного вызова конструктора суперкласса.

  • Если у C есть хотя бы один инициализатор экземпляра или инициализатор переменной экземпляра, то V [не] присвоен после явного или неявного вызова конструктора суперкласса, если и только если V [не] присвоен после правого инициализатора экземпляра или инициализатора переменной экземпляра класса C.

Пусть C — класс, а V — пустое final поле члена класса C, объявленное в суперклассе C. Тогда:

  • V определённо присвоен (и, более того, не определённо не присвоен) перед блоком, являющимся телом конструктора или инициализатора экземпляра класса C.

  • V определённо присвоен (и, более того, не определённо не присвоен) перед каждым инициализатором переменной экземпляра класса C.

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

Spec-Zone.ru

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