В области метода или класса следующая строка компилируется (с предупреждением):
int x = x = 1;
В области класса, где переменные получают значения по умолчанию , следующее дает ошибку «неопределенная ссылка»:
int x = x + 1;
Разве не первая x = x = 1
должна закончиться той же ошибкой «неопределенная ссылка»? А может вторая строчка int x = x + 1
должна компилироваться? Или мне чего-то не хватает?
java
compiler-construction
Марчин
источник
источник
static
в переменную области класса, напримерstatic int x = x + 1;
, получите ли вы ту же ошибку? Потому что в C # есть разница, статическая она или нестатическая.static int x = x + 1
не работает на Java.int a = this.a + 1;
иint b = 1; int a = b + 1;
в области видимости класса (и то, и другое нормально в Java) происходит сбой, вероятно, из-за §17.4.5.2 - «Инициализатор переменной для поля экземпляра не может ссылаться на создаваемый экземпляр». Я не знаю, разрешено ли это где-то явно, но static не имеет такого ограничения. В Java правила другие, и ониstatic int x = x + 1
не работают по той же причине, чтоint x = x + 1
иОтветы:
tl; dr
Для полей ,
int b = b + 1
является незаконным , посколькуb
нелегальной вперед ссылкаb
. Вы можете исправить это, написавint b = this.b + 1
, что компилируется без нареканий.Для локальных переменных ,
int d = d + 1
является незаконным , посколькуd
не инициализируется перед использованием. Это не относится к полям, которые всегда инициализируются по умолчанию.Вы можете увидеть разницу, попытавшись скомпилировать
int x = (x = 1) + x;
как объявление поля и как объявление локальной переменной. Первое не удастся, но второе удастся из-за разницы в семантике.
Введение
Во-первых, правила для инициализаторов полей и локальных переменных очень разные. Итак, в этом ответе правила разбиты на две части.
Мы будем использовать эту тестовую программу повсюду:
Объявление
b
недействительно и завершаетсяillegal forward reference
ошибкой.Объявление
d
недействительно и не выполняется сvariable d might not have been initialized
ошибкой.Тот факт, что эти ошибки разные, должен указывать на то, что причины ошибок также разные.
Поля
Инициализаторы полей в Java регулируются JLS §8.3.2 , Инициализация полей.
Объем поля определяется в JLS §6.3 , Область декларации.
Соответствующие правила:
m
объявленного или унаследованного от типа класса C (§8.1.6), - это все тело C, включая любые объявления вложенных типов.В §8.3.2.3 говорится:
Фактически вы можете ссылаться на поля до того, как они были объявлены, за исключением некоторых случаев. Эти ограничения предназначены для предотвращения кода вроде
от компиляции. В спецификации Java говорится, что «указанные выше ограничения предназначены для перехвата во время компиляции циклических или иным образом искаженных инициализаций».
К чему на самом деле сводятся эти правила?
Короче говоря, правила в основном говорят, что вы должны объявить поле перед ссылкой на это поле, если (а) ссылка находится в инициализаторе, (б) ссылка не назначается, (в) ссылка является простое имя (без таких квалификаторов
this.
) и (d) к нему нет доступа из внутреннего класса. Таким образом, прямая ссылка, удовлетворяющая всем четырем условиям, является недопустимой, но прямая ссылка, которая не работает хотя бы по одному условию, является допустимой.int a = a = 1;
компилируется , потому что он нарушает (б): ссылкаa
будет быть назначена, так что это законно , чтобы обратиться кa
заранееa
«s полной декларации.int b = this.b + 1
также компилируется, потому что нарушает (c): ссылкаthis.b
не является простым именем (оно дополненоthis.
). Эта странная конструкция по-прежнему четко определена, посколькуthis.b
имеет нулевое значение.Итак, в основном, ограничения на ссылки на поля в инициализаторах препятствуют
int a = a + 1
успешной компиляции.Заметим , что объявление поля
int b = (b = 1) + b
будет не в состоянии компиляции, так как окончательныйb
по - прежнему является незаконным опережающей ссылкой.Локальные переменные
Объявления локальных переменных регулируются JLS §14.4 , Заявления объявления локальных переменных.
Сфера локальной переменной определяется в JLS §6.3 , Область декларации:
Обратите внимание, что инициализаторы находятся в пределах объявляемой переменной. Так почему не
int d = d + 1;
компилируется?Причина заключается в правиле Java об определенном назначении ( JLS §16 ). Определенное присвоение в основном говорит о том, что каждый доступ к локальной переменной должен иметь предшествующее присвоение этой переменной, а компилятор Java проверяет циклы и ветки, чтобы гарантировать, что присвоение всегда происходит до любого использования (вот почему определенному назначению посвящен целый раздел спецификации к нему). Основное правило:
x
,x
должны быть определенно присвоенной перед въездом, или происходит ошибка времени компиляции.В
int d = d + 1;
, доступ кd
разрешен для локальной переменной штраф, но, посколькуd
не был назначен доd
обращения, компилятор выдает ошибку. Вint c = c = 1
,c = 1
происходит во- первых, который назначаетc
, а затемc
инициализируется в результате этого присваивания (который является 1).Обратите внимание, что из-за определенных правил присваивания объявление локальной переменной
int d = (d = 1) + d;
будет успешно скомпилировано (в отличие от объявления поляint b = (b = 1) + b
), потому чтоd
оно определенно присваивается к моменту достижения финалаd
.источник
int b = b + 1
b находится справа (не слева) от задания, поэтому он нарушит это ...int x = x = 1
, в котором случае ничего из этого не применимо.эквивалентно
пока в
сначала нам нужно вычислить,
x+1
но значение x неизвестно, поэтому вы получите ошибку (компилятор знает, что значение x неизвестно)источник
int x = x = 1;
эквивалентноint x = (x = 1)
, неx = 1; x = x;
. Вы не должны получать предупреждение компилятора об этом.int x = x = 1;
s эквивалентно intx = (x = 1)
из-за=
int x = (x = 1)
эквивалентенint x; x = 1; x = x;
(объявление переменной, оценка инициализатора поля, присвоение переменной результату указанной оценки), отсюда и предупреждениеЭто примерно эквивалентно:
Во-первых,
int <var> = <expression>;
всегда эквивалентноВ этом случае ваше выражение -
x = 1
это тоже утверждение.x = 1
является допустимым утверждением, поскольку переменнаяx
уже была объявлена. Это также выражение со значением 1, которое затемx
снова присваивается .источник
0
значение по умолчанию для целых чисел, поэтому я ожидал, что результат будет 1, а неundefined reference
.x + 1
нет определенного значения, потому чтоx
он неинициализирован.x
это определяется как переменная члена ( «в области видимости класса»).В java или на любом современном языке назначение происходит справа.
Предположим, у вас есть две переменные x и y,
Этот оператор действителен, и именно так компилятор их разбивает.
Но в твоем случае
Компилятор выдал исключение, потому что он разделяется вот так.
источник
int x = x = 1;
не равно:javap снова помогает нам, это инструкции JVM, сгенерированные для этого кода:
скорее:
Нет причин выдавать неопределенную ссылку на ошибку. Теперь переменная используется до ее инициализации, поэтому этот код полностью соответствует спецификации. На самом деле здесь вообще нет использования переменных , только присваивания. И JIT-компилятор пойдет еще дальше, он устранит такие конструкции. Честно говоря, я не понимаю, как этот код связан со спецификацией JLS для инициализации и использования переменных. Без использования никаких проблем. ;)
Пожалуйста, поправьте, если я ошибаюсь. Я не могу понять, почему другие ответы, которые относятся ко многим параграфам JLS, собирают так много плюсов. Эти абзацы не имеют ничего общего с данным случаем. Всего два последовательных задания и не более того.
Если мы напишем:
равно:
Самое правое выражение просто присваивается переменным одну за другой, без какой-либо рекурсии. Мы можем путать переменные как угодно:
источник
В
int x = x + 1;
добавлении 1 к й, так что значениеx
, это еще не создано.Но in
int x=x=1;
будет компилироваться без ошибок, потому что вы присваиваете 1x
.источник
Ваш первый фрагмент кода содержит второй
=
вместо плюса. Это будет компилироваться где угодно, в то время как второй фрагмент кода не будет компилироваться ни в одном месте.источник
Во втором фрагменте кода x используется перед его объявлением, а в первом фрагменте кода он просто присваивается дважды, что не имеет смысла, но является допустимым.
источник
Давайте разберемся по шагам, правильно ассоциативно
x = 1
присвоить 1 переменной xint x = x
присвоить самому себе значение x как int. Поскольку x ранее был назначен как 1, он сохраняет 1, хотя и в избыточной форме.Это нормально компилируется.
x + 1
, добавьте единицу к переменной x. Однако, если x не определен, это вызовет ошибку компиляции.int x = x + 1
, таким образом, эта строка компилирует ошибки, поскольку правая часть равных не будет компилировать добавление единицы к неназначенной переменнойисточник
=
оператора, поэтому это то же самое, что иint x = (x = 1);
.Второй
int x=x=1
- это компиляция, потому что вы присваиваете значение x, но в другом случаеint x=x+1
здесь переменная x не инициализируется. Помните, что в локальной переменной java не инициализируется значение по умолчанию. Примечание. Если он (int x=x+1
) также находится в области класса, тогда также будет выдана ошибка компиляции, поскольку переменная не создается.источник
успешно компилируется в Visual Studio 2008 с предупреждением
источник
c
вместо тега,java
но, видимо, это был другой вопрос.bool y;
иy==true
вернет false.void main() { int x = x + 1; printf("%d ", x); }
в Visual Studio 2008, в Debug я получаю исключение,Run-Time Check Failure #3 - The variable 'x' is being used without being initialized.
а в Release я получаю номер,1896199921
напечатанный в консоли.static
поля (статическая переменная уровня класса) применяются те же правила. Например, поле, объявленное какpublic static int x = x + 1;
компилируемое без предупреждения в Visual C #. Возможно то же самое на Java?x не инициализируется в
x = x + 1
;.Язык программирования Java является статически типизированным, что означает, что все переменные должны быть сначала объявлены, прежде чем их можно будет использовать.
См. Примитивные типы данных
источник
Строка кода не компилируется с предупреждением из-за того, как код действительно работает. Когда вы запускаете код
int x = x = 1
, Java сначала создает переменнуюx
, как определено. Затем запускается код присвоения (x = 1
). Посколькуx
он уже определен, в системе нет ошибок, для которых установленоx
значение 1. Это возвращает значение 1, потому что теперь это значениеx
. Таким образом,x
теперь окончательно установлено значение 1.Java в основном выполняет код, как если бы он был следующим:
Тем не менее, в своей второй части кода,
int x = x + 1
, то+ 1
утверждение требует ,x
чтобы определить, что к тому времени это не так . Поскольку операторы присваивания всегда означают, что код справа от=
них запускается первым, код завершится ошибкой, посколькуx
он не определен. Java будет запускать такой код:источник
Комплиер читал утверждения справа налево, а мы сделали обратное. Поэтому сначала это раздражало. Сделайте это привычкой читать операторы (код) справа налево, у вас не будет такой проблемы.
источник