Spec-Zone .ru
спецификации, руководства, описания, API
Содержание | Предыдущий | Следующий | Индекс Спецификация языка Java
Третий Выпуск


ГЛАВА 16

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


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

Точно так же каждый пробел final переменная должна быть присвоена самое большее однажды; это должно быть определенно неприсвоенным, когда присвоение на это происходит. Такое присвоение определяется, чтобы произойти если и только если или простое имя переменной, или ее простое имя, квалифицированное this, происходит на левой стороне оператора присваивания. Компилятор 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.10), который 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, чтобы представить операторы. Мы будем использовать фразу V, чтобы означать что или простое имя переменной V, или V `s простое имя, квалифицированное this (игнорирование круглых скобок). Мы будем использовать фразу не V, чтобы означать отрицание V.

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 Другие Выражения булевской переменной Типа

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

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

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

Отметьте это, если V и V определенно не присваивается перед составным присвоением, таким как a &= b, затем ошибка времени компиляции обязательно произойдет. Первое правило для определенного вышеизложенного присвоения включает разобщенное "V" даже для составных выражений присвоения, не только простых присвоений, так, чтобы V, как полагали, был определенно присвоен в более поздних точках в коде. Включая разобщенное "V" не влияет на выбор из двух альтернатив относительно того, является ли программа приемлемой или приведет к ошибке времени компиляции, но это влияет, сколько различных точек в коде может быть расценено как ошибочные, и так практически это может улучшить качество сообщения об ошибке. Подобный комментарий применяется к включению соединенного "не V" в первом правиле для определенного вышеизложенного неприсвоения.

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

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

Если выражение не является булевым константным выражением, и не является предынкрементным выражением ++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.7.1) обрабатываются особенно (§16.9); хотя они синтаксически подобны операторам выражения, содержащим вызовы метода, они не операторы выражения, и поэтому правила этого раздела не применяются к явным вызовам конструктора.

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

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

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

16.2.2 Блоки

Мы говорим, что V является определенно неприсвоенным всюду в блоке B эквивалентность

Эти условия парадоксальны и требуют некоторого объяснения. Рассмотрите простое присвоение V = e. Если V определенно присваивается после e, тогда также:

  1. Присвоение происходит в невыполняемом коде, и V vacouusly, определенно присвоенный. В этом случае присвоение не будет фактически иметь место, и мы можем принять это V не присваивается выражением присвоения.
  2. V был уже присвоен более ранним выражением до e. В этом случае текущее присвоение вызовет ошибку времени компиляции.

Так, мы можем прийти к заключению что, если условия соблюдает программа, которая не вызывает ошибки времени компиляции, тогда любые присвоения на V в B не будет фактически иметь место во время выполнения.

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

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

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

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

16.2.7 если Операторы

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

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

16.2.8 операторы контроля

Следующие правила применяют обоих к оператору assert e1 и к оператору assert e1:e2:

Следующее правило применяется к оператору assert e1: e2:

16.2.9 операторы переключения

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

16.2.10 в то время как Операторы

16.2.11 сделайте Операторы

16.2.12 для Операторов

Правила здесь касаются основного for оператор (§14.14.1). Начиная с улучшенного for (§14.14.2) оператор определяется traslation к основному for оператор, никакие специальные правила не должны быть обеспечены для этого.

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

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

16.2.13 повредите, продолжайте, возвратите, и бросьте Операторы

16.2.14 синхронизируемые Операторы

16.2.15 попробуйте Операторы

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

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

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

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

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

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

Правилам, определяющим, когда переменная определенно присваивается или определенно неприсвоенная перед перечислимой константой, дают §16.8.


Обсуждение

Это - то, потому что перечислимая константа является по существу статическим заключительным полем (§8.3.1.1, §8.3.1.2), который инициализируется с выражением создания экземпляра класса (§15.9).


Определенным присвоением/нестатусом присвоения вакансии любой конструкции в пределах тела класса перечислимой константы управляют обычные правила для классов.

Позвольте y быть параметром перечислимой константы, но не первым. Затем:

Иначе:

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


Обсуждение

Это должно быть четким, что, если анонимный класс неявно определяется перечислимой константой, правила раздела §16.5 применяются.


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

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

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

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

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

Отметьте, что нет никаких правил, которые позволили бы нам приходить к заключению, что V является определенно неприсвоенным перед инициализатором статической переменной или перечислимой константой. Мы можем неофициально прийти к заключению, что V не является определенно неприсвоенным ни перед каким инициализатором статической переменной C, но нет никакой потребности в таком правиле, которое будет утверждено явно.

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

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

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

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

Отметьте, что нет никаких правил, которые позволили бы нам приходить к заключению, что V является определенно неприсвоенным перед инициализатором переменной экземпляра. Мы можем неофициально прийти к заключению, что V не является определенно неприсвоенным ни перед каким инициализатором переменной экземпляра C, но нет никакой потребности в таком правиле, которое будет утверждено явно.

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

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

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


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

Авторское право © 1996-2005 Sun Microsystems, Inc. Все права защищены
Пожалуйста, отправьте любые комментарии или исправления через нашу форму обратной связи

free hit counter