Глава 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.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 (. Для определения того, является ли V определённо не присвоено в каком-либо подвыражении e) Se, нам нужно определить, является ли 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.
-
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).
-
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когда ложно.
-
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когда ложно.
-
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.
Предположим, что 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когда ложно.
Предположим, что 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при ложном значении.
Предположим, что e — это выражение типа boolean, которое не является булевой константой, выражением логического отрицания !a, выражением условной конъюнкции a && b, выражением условной дизъюнкции a || b или условным выражением a ? b : c.
-
V [присвоено/не присвоено] после
eпри истинном значении тогда и только тогда, когда V [присвоено/не присвоено] послеe. -
V [присвоено/не присвоено] после
eпри ложном значении тогда и только тогда, когда V [присвоено/не присвоено] послеe.
Рассмотрим выражение присваивания 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" в первом правиле для определённого неприсваивания, описанном выше.
-
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--.
Если выражение не является булевым константным выражением и не является выражением с прединкрементом ++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.
-
V [присвоен(а)] [не присвоен(а)] после оператора без выражения (§14.6) тогда и только тогда, когда он [присвоен(а)] [не присвоен(а)] до оператора без выражения.
-
Поле-член 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 фактически не произойдут во время выполнения.
-
V [присвоен(а)] [не присвоен(а)] после оператора объявления локального класса (§14.3) тогда и только тогда, когда V [присвоен(а)] [не присвоен(а)] перед оператором объявления локального класса.
-
V [присвоен(а)] [не присвоен(а)] после оператора объявления локальной переменной, не содержащего инициализаторов переменных, тогда и только тогда, когда V [присвоен(а)] [не присвоен(а)] перед оператором объявления локальной переменной.
-
V определённо присвоена после оператора объявления локальной переменной, содержащего по крайней мере один инициализатор переменной, если либо V определённо присвоена после последнего инициализатора переменной в операторе объявления локальной переменной, либо последний инициализатор переменной в объявлении находится в деклараторе, который объявляет V.
-
V определённо не присвоена после оператора объявления локальной переменной, содержащего по крайней мере один инициализатор переменной, если V определённо не присвоена после последнего инициализатора переменной в операторе объявления локальной переменной и последний инициализатор переменной в объявлении не находится в деклараторе, который объявляет V.
-
V [присвоен(а)] [не присвоен(а)] перед первым инициализатором переменной в операторе объявления локальной переменной тогда и только тогда, когда V [присвоен(а)] [не присвоен(а)] перед оператором объявления локальной переменной.
-
V определённо присвоена перед любым инициализатором переменной
e, отличным от первого, в операторе объявления локальной переменной, если либо V определённо присвоена после инициализатора переменной слева отe, либо выражение инициализации слева отeнаходится в деклараторе, который объявляет V. -
V определённо не присвоена перед любым инициализатором переменной
e, отличным от первого, в операторе объявления локальной переменной, если V определённо не присвоена после инициализатора переменной слева отeи выражение инициализации слева отeне находится в деклараторе, который объявляет V.
-
V [присвоен(а)] [не присвоен(а)] после оператора с меткой
L:S (гдеL— метка) (§14.7) тогда и только тогда, когда V [присвоен(а)] [не присвоен(а)] после S и V [присвоен(а)] [не присвоен(а)] перед каждым операторомbreak, который может завершить оператор с меткойL:S. -
V [присвоен(а)] [не присвоен(а)] перед S тогда и только тогда, когда V [присвоен(а)] [не присвоен(а)] перед
L:S.
-
V является [не]присвоенным после оператора выражения
e;(§14.8) тогда и только тогда, когда он является [не]присвоенным послеe. -
V является [не]присвоенным перед
eтогда и только тогда, когда он является [не]присвоенным передe;.
Следующие правила применяются к оператору if ( (§14.9.1) : e) S
-
V является [не]присвоенным после
if (тогда и только тогда, когда V является [не]присвоенным после S и V является [не]присвоенным послеe) Seв случае ложности. -
V является [не]присвоенным перед
eтогда и только тогда, когда V является [не]присвоенным передif (.e) S -
V является [не]присвоенным перед S тогда и только тогда, когда V является [не]присвоенным после
eв случае истинности.
Следующие правила применяются к оператору if (e) S else T (§14.9.2) :
-
V является [не]присвоенным после
if (тогда и только тогда, когда V является [не]присвоенным после S и V является [не]присвоенным после T.e) S else T -
V является [не]присвоенным перед
eтогда и только тогда, когда V является [не]присвоенным передif (.e) S else T -
V является [не]присвоенным перед S тогда и только тогда, когда V является [не]присвоенным после
eв случае истинности. -
V является [не]присвоенным перед T тогда и только тогда, когда V является [не]присвоенным после
eв случае ложности.
Следующие правила применяются как к оператору assert , так и к оператору e1assert (§14.10) : e1 : e2
-
V является [не]присвоенным перед
e1тогда и только тогда, когда V является [не]присвоенным перед операторомassert. -
V определённо присвоен после оператора
assertтогда и только тогда, когда V определённо присвоен перед операторомassert. -
V определённо не присвоен после оператора
assertтогда и только тогда, когда V определённо не присвоен перед операторомassertи V определённо не присвоен послеe1в случае истинности.
Следующее правило применяется к оператору assert : e1 : e2
-
V является [не]присвоенным перед
e2тогда и только тогда, когда V является [не]присвоенным послеe1в случае ложности.
-
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 является [не]присвоенным после предыдущего оператора.
-
V является [не]присвоенным после оператора
while ((§14.12) тогда и только тогда, когда V является [не]присвоенным послеe) Seв случае ложности и 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в случае истинности.
-
V является [не]присвоенным после оператора
do S while ((§14.13) тогда и только тогда, когда V является [не]присвоенным послеe);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.
Правила здесь охватывают базовый 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.
-
Если начальная часть
forоператора является оператором объявления локальной переменной, применяются правила из §16.2.4. -
В противном случае, если начальная часть пустая, то V считается [не]присвоенным после начальной части, если V считается [не]присвоенным перед начальной частью.
-
В противном случае применяются три правила:
-
V считается [не]присвоенным после начальной части, если V считается [не]присвоенным после последнего оператора выражения в начальной части.
-
V считается [не]присвоенным перед первым оператором выражения в начальной части, если V считается [не]присвоенным перед начальной частью.
-
V считается [не]присвоенным перед оператором выражения S (кроме первого) в начальной части, если V считается [не]присвоенным после оператора выражения, непосредственно предшествующего S.
-
-
Если часть инкрементации
forоператора пустая, то V считается [не]присвоенным после части инкрементации, если V считается [не]присвоенным перед частью инкрементации. -
В противном случае применяются три правила:
-
V считается [не]присвоенным после части инкрементации, если V считается [не]присвоенным после последнего оператора выражения в части инкрементации.
-
V считается [не]присвоенным перед первым оператором выражения в части инкрементации, если V считается [не]присвоенным перед частью инкрементации.
-
V считается [не]присвоенным перед оператором выражения S (кроме первого) в части инкрементации, если V считается [не]присвоенным после оператора выражения, непосредственно предшествующего S.
-
-
По соглашению, считается, что 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оператором.
-
V считается [не]присвоенным после
synchronized ((§14.19), если V считается [не]присвоенным после S.e) S -
V считается [не]присвоенным перед
e, если V считается [не]присвоенным перед операторомsynchronized (.e) S -
V считается [не]присвоенным перед S, если V считается [не]присвоенным после
e.
Эти правила применяются к каждому try оператору (§14.20), независимо от наличия finally блока:
-
V [инициализирован/не инициализирован] до
tryблока тогда и только тогда, когда V [инициализирован/не инициализирован] доtryоператора. -
V определённо инициализирован до
catchблока тогда и только тогда, когда V определённо инициализирован доtryблока. -
V определённо не инициализирован до
catchблока, если выполняются все следующие условия:-
V определённо не инициализирован после
tryблока. -
V определённо не инициализирован до каждого
returnоператора, который принадлежитtryблоку. -
V определённо не инициализирован после
eв каждом операторе видаthrowe, который принадлежит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в каждом операторе видаthrowe, который принадлежитtryблоку. -
V определённо не инициализирован после каждого
assertоператора, который находится вtryблоке. -
V определённо не инициализирован до каждого
breakоператора, который принадлежитtryблоку и чей целевой оператор break содержит (или является)tryоператором. -
V определённо не инициализирован до каждого
continueоператора, который принадлежитtryблоку и чей целевой оператор continue содержитtryоператор. -
V определённо не инициализирован после каждого
catchблокаtryоператора.
-
-
Формальный параметр V метода или конструктора (§8.4.1, §8.8.1) определённо инициализирован (и, более того, не определённо не инициализирован) до тела метода или конструктора.
-
Параметр исключения V
catchблока (§14.20) определённо инициализирован (и, более того, не определённо не инициализирован) до телаcatchблока.
-
V [инициализирован/не инициализирован] после пустого инициализатора массива (§10.6) тогда и только тогда, когда V [инициализирован/не инициализирован] до пустого инициализатора массива.
-
V [инициализирован/не инициализирован] после непустого инициализатора массива, если V [инициализирован/не инициализирован] после последнего инициализатора переменной в инициализаторе массива.
-
V [инициализирован/не инициализирован] до первого инициализатора переменной инициализатора массива, если V [инициализирован/не инициализирован] до инициализатора массива.
-
V [инициализирован/не инициализирован] до любого другого инициализатора переменной
eинициализатора массива, если V [инициализирован/не инициализирован] после инициализатора переменной слева отeв инициализаторе массива.
Правила, определяющие, когда переменная определённо инициализирована или определённо не инициализирована до константы перечисления (§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.
-
V обязательно инициализируется до объявления анонимного класса (§15.9.5), объявленного в области видимости V, если V инициализируется после выражения создания экземпляра класса, объявляющего анонимный класс.
Должно быть ясно, что если анонимный класс неявно определяется константой перечисления, применяются правила §16.5.
Пусть C — класс, а V — пустое final поле-член класса C. Тогда:
Пусть C — класс, объявленный в области видимости V. Тогда:
-
V инициализируется до объявления типа члена C, если V инициализируется до объявления C.
Пусть 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.
Пусть 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.