Глава 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 понятие определённой неприсвоенности применяется только к переменным без начального значения. Если V — локальная переменная без начального значения, то только метод, к которому относится её объявление, может выполнить присваивание переменной V. Если V — поле без начального значения, то только конструктор или инициализатор класса, содержащий объявление для V, может выполнить присваивание переменной V; никакой метод не может выполнить присваивание переменной V. Наконец, явные вызовы конструкторов (§8.8.7.1) обрабатываются особо (§16.9); хотя они синтаксически похожи на операторы выражений, содержащие вызовы методов, они не являются операторами выражений, и поэтому правила этого раздела не применяются к явным вызовам конструкторов.
Если выражение является лямбда-выражением, то применяются следующие правила:
-
V является [не]присвоенным после выражения тогда и только тогда, когда V является [не]присвоенным перед выражением.
-
V определённо присвоено перед выражением или блоком, который является телом лямбды (§15.27.2), если V определённо присвоено перед лямбда-выражением.
Никакое правило не позволяет V быть определённо неприсвоенным перед телом лямбды. Это сделано намеренно: переменная, которая была определённо неприсвоенной перед телом лямбды, может оказаться присвоенной позже, поэтому мы не можем сделать вывод, что переменная будет неприсвоенной при выполнении тела.
Для любого непосредственного подвыражения y выражения x, где 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 без значения (
final) определённо инициализировано (и, более того, не определённо неинициализировано) перед блоком (§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 определённо не инициализирован после блока инкрементаforоператора.
-
-
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.