Содержание | Предыдущий | Следующий | Индекс Спецификация языка Java
Второй Выпуск


ГЛАВА 16

Определенное Присвоение


Каждая локальная переменная (§14.4) и каждый пробел final у поля (§4.5.4) (§8.3.1.2) должно быть определенно присвоенное значение, когда любой доступ его значения происходит. Компилятор Java должен выполнить определенный консервативный анализ потоков, чтобы удостовериться что для каждого доступа локальной переменной или пробела final поле f, f определенно присваивается перед доступом; иначе ошибка времени компиляции должна произойти.

Точно так же каждый пробел final переменная должна быть присвоена самое большее однажды; это должно быть определенно неприсвоенным, когда присвоение на это происходит. Компилятор Java должен выполнить определенный консервативный анализ потоков, чтобы удостовериться что для каждого присвоения на пробел final переменная, переменная является определенно неприсвоенной перед присвоением; иначе ошибка времени компиляции должна произойти.

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

Идея позади определенного присвоения состоит в том что присвоение на локальную переменную или пробел final поле должно произойти на каждом возможном пути выполнения к доступу. Точно так же идея позади определенного неприсвоения состоит в том что никакое другое присвоение на пробел final переменной разрешают произойти на любом возможном пути выполнения к присвоению. Анализ принимает во внимание структуру операторов и выражений; это также обеспечивает специальный режим операторов выражения !, &&, ||, и ? :, и оцененных булевской переменной константных выражений.

Например, компилятор Java распознает это k определенно присваивается перед его доступом (как параметр вызова метода) в коде:

{
	int k;
	if (v > 0 && (k = System.in.read()) >= 0)
		System.out.println(k);
}
потому что доступ происходит только если значение выражения:

v > 0 && (k = System.in.read()) >= 0
истина, и значение может быть 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
}
должен быть отклонен компилятором Java, потому что в этом случае while оператор, как гарантируют, не выполнит свое тело, насколько правила определенного присвоения затрагиваются.

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

Например, компилятор Java должен произвести ошибку времени компиляции для кода:

{
	int k;
	int n = 5;
	if (n > 2)
		k = 3;
	System.out.println(k);	// k is not "definitely assigned" before this
}
даже при том, что значение 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 присваивается независимо от того, является ли флаг 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 here
}
и так компиляция этой программы должна заставить ошибку времени компиляции происходить.

Связанный пример иллюстрирует правила определенного неприсвоения. Компилятор 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 присваивается самое большее однажды (действительно, точно однажды) независимо от того, является ли флаг true или false. Но правила не принимают изменение:

void unflow(boolean flag) {
	final int k;
	if (flag) {
		k = 3;
		System.out.println(k);
	}
	if (!flag) {
		k = 4; 		// k is not "definitely unassigned" before here
		System.out.println(k);
	}
}
и так компиляция этой программы должна заставить ошибку времени компиляции происходить.

Чтобы точно определить все случаи определенного присвоения, правила в этом разделе определяют несколько технических терминов:

Для оцененных булевской переменной выражений последние два усовершенствованы в четыре случая:

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

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

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

Фраза "V определенно присваивается, после X" (то, где V локальная переменная и X, является оператором или выражением) означает "V, определенно присваивается после X, если X обычно завершается". Если X завершается резко, присвоение не должно произойти, и правила, утвержденные здесь, принимают это во внимание. Специфическое последствие этого определения - то, что "V определенно присваивается после break;"всегда истина! Поскольку a break оператор никогда обычно не завершается, это вырождено истинно, что V был присвоен значение если break оператор обычно завершается.

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

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

Чтобы сократить правила, общепринятое сокращение "эквивалентность" используется, чтобы означать "если и только если". Мы также используем соглашение сокращения: если правило содержит одно или более возникновений" [un] присвоенный", тогда оно обозначает два правила, один с каждым возникновением" [un] присвоенный" замененный "определенно присвоенным" и один с каждым возникновением" [un] присвоенный" замененный "определенно неприсвоенным".

Например:

V [un], присвоенный после пустой эквивалентности оператора, которая это [un], присвоенный перед пустым оператором.

как должны понимать, обозначает два правила:

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

Пересмотренное правило было бы: "V является определенно неприсвоенным, прежде e эквивалентность это является определенно неприсвоенным перед, в то время как оператор и определенно отменил присвоение после S". Однако, когда мы формулируем правило для S, мы находим: "V является определенно неприсвоенным, прежде S эквивалентность это является определенно неприсвоенным после e когда истина". Это приводит к зацикливанию. В действительности, V является определенно неприсвоенным перед условием цикла e, только если оно является неприсвоенным после цикла в целом!

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

Принятие V является определенно неприсвоенным, прежде e, V является определенно неприсвоенным после e.

Изменения на вышеупомянутом анализе используются, чтобы определить хорошо основанные определенные правила неприсвоения для всех операторов цикла на языке.

Всюду по остальной части этой главы мы, если явно не утверждено иначе, запишем V, чтобы представить локальную переменную или пробел final поле (для правил определенного присвоения) или пробел final переменная (для правил определенного неприсвоения). Аналогично, мы будем использовать a, b, c, и e, чтобы представить выражения, и S и T, чтобы представить операторы.

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

16.1.1 Булевы Константные выражения

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

16.1.2 Булев оператор &&

16.1.3 Булев оператор ||

16.1.4 Булев оператор!

16.1.5 Булев оператор?:

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

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

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

16.1.7 Выражения присвоения

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

Если выражение присвоения оценивается булевской переменной, то эти правила также применяются:

16.1.8 Операторы ++ и--

16.1.9 Другие Выражения

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

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

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

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

16.2.1 Пустые Операторы

16.2.2 Блоки

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

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

16.2.5 Помеченные операторы

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

16.2.7 if Операторы

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

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

16.2.8 switch Операторы

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

16.2.9 while Операторы

16.2.10 do Операторы

16.2.11 for Операторы

16.2.11.1 Часть инициализации

16.2.11.2 Часть приращения

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

16.2.13 synchronized Операторы

16.2.14 try Операторы

Эти правила применяются к каждому try оператор, есть ли у этого a finally блок:

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

Если a try у оператора действительно есть a finally блок, тогда эти правила также применяются:

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

16.4 Определенные Инициализаторы Присвоения и Массива

16.5 Определенное Присвоение и Анонимные классы

16.6 Определенное Присвоение и Типы элемента

Позвольте C быть классом, объявленным в рамках V. Затем:

16.7 Определенное Присвоение и Статические Инициализаторы

Позвольте C быть классом, объявленным в рамках V. Затем:

Позвольте C быть классом, и позволять V быть пробелом final static задействованное поле C, объявленного в C. Затем:

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

16.8 Определенное Присвоение, Конструкторы, и Инициализаторы Экземпляра

Позвольте C быть классом, объявленным в рамках V. Затем:

Позвольте C быть классом, и позволять V быть пробелом final не -static задействованное поле C, объявленного в C. Затем:

Следующие правила содержат в пределах конструкторов класса C:

Позвольте C быть классом, и позволять V быть пробелом final задействованное поле C, объявленного в суперклассе C. Затем:


Содержание | Предыдущий | Следующий | Индекс Спецификация языка Java
Второй Выпуск
Авторское право © Sun Microsystems, Inc 2000 года. Все права защищены
Пожалуйста, отправьте любые комментарии или исправления к jls@java.sun.com



Spec-Zone.ru - all specs in one place