Глава 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 поля, которое находится в области видимости (§6.3). Аналогично, мы будем использовать 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 if ( (§14.9.1): e) S
-
V является [не]инициализированным после
if (тогда и только тогда, когда V является [не]инициализированным после S и V является [не]инициализированным послеe) Seпри ложном условии. -
V является [не]инициализированным перед
eтогда и только тогда, когда V является [не]инициализированным передif (.e) S -
V является [не]инициализированным перед S тогда и только тогда, когда V является [не]инициализированным после
eпри истинном условии.
Следующие правила применяются к оператору if-then-else 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 assert
, так и к оператору 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
switch(§14.11) тогда и только тогда, когда все из следующего истинны:-
Есть метка case в блоке switch или V является [не]инициализированным после выражения switch.
-
Нет меток case в блоке switch, которые не начинаются с блока-группы операторов (т.е. нет меток case непосредственно перед "
}" завершающим блок switch) или V является [не]инициализированным после выражения switch. -
Блок switch не содержит блоков-групп операторов или V является [не]инициализированным после последнего оператора в последней группе блоков.
-
V является [не]инициализированным перед каждым оператором
break, который может выйти из оператора switchswitch.
-
-
V является [не]инициализированным перед выражением switch тогда и только тогда, когда V является [не]инициализированным перед оператором
switch.
Если блок switch содержит хотя бы одну группу блоков, то также применяются следующие правила:
-
V является [не]инициализированным перед первым оператором первой группы блоков в блоке switch тогда и только тогда, когда V является [не]инициализированным после выражения switch.
-
V является [не]инициализированным перед первым оператором любой группы блоков, кроме первой, тогда и только тогда, когда V является [не]инициализированным после выражения switch и V является [не]инициализированным после предыдущего оператора.
-
V является [не]инициализированным после оператора while
while ((§14.12) тогда и только тогда, когда V является [не]инициализированным послеe) Seпри ложном условии и V является [не]инициализированным перед каждым операторомbreak, для которого операторwhileявляется точкой прерывания. -
V определённо инициализирован перед
eтогда и только тогда, когда V определённо инициализирован перед операторомwhile. -
V определённо не инициализирован перед
eтогда и только тогда, когда все из следующего истинны:-
V определённо не инициализирован перед оператором
while. -
Предполагая, что V определённо не инициализирован перед
e, V определённо не инициализирован после S. -
Предполагая, что V определённо не инициализирован перед
e, V определённо не инициализирован перед каждым операторомcontinue, для которого операторwhileявляется точкой продолжения.
-
-
V является [не]инициализированным перед S тогда и только тогда, когда V является [не]инициализированным после
eпри истинном условии.
-
V является [не]инициализированным после оператора do
do S while ((§14.13) тогда и только тогда, когда V является [не]инициализированным послеe);eпри ложном условии и V является [не]инициализированным перед каждым операторомbreak, для которого операторdoявляется точкой прерывания. -
V определённо инициализирован перед S тогда и только тогда, когда V определённо инициализирован перед оператором
do. -
V определённо не инициализирован перед S тогда и только тогда, когда все из следующего истинны:
-
V определённо не инициализирован перед оператором
do. -
Предполагая, что V определённо не инициализирован перед S, V определённо не инициализирован после
eпри истинном условии.
-
-
V является [не]инициализированным перед
eтогда и только тогда, когда V является [не]инициализированным после S и V является [не]инициализированным перед каждым операторомcontinue, для которого операторdoявляется точкой продолжения.
Приведённые правила охватывают базовый 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) класса 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) класса 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.