Spec-Zone.ru › Java Language Specification 7

Глава 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 поля (для правил определённого присваивания) или пустой final переменной (для правил определённого неприсваивания). Аналогично, мы будем использовать 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 (e) S (§14.9.1) :

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

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

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

Следующие правила применяются к оператору 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 e1, так и к оператору 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 (§14.11) тогда и только тогда, когда выполняются все следующие условия:

    • В блоке switch имеется метка default или V является [не]присвоенным после выражения switch.

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

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

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

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

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

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

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

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

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

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

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

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

    • При предположении, что V определённо не присвоен перед e, V определённо не присвоен после S.

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

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

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

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

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

  • V определённо не присвоен перед S если выполняются все следующие условия:

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

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

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

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.1) класса 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.

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

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

  • V инициализируется перед инициализатором переменной экземпляра (§8.3.2.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