Почему переменная инициализация выражения присваивания [String x = (x = y)] компилируется?
Как это компилируется без ошибок? Как я понимаю, компилятор проверяет тип переменной (в данном случае String
), а затем видит, соответствует ли тип выражения с правой стороны типу переменной (или, по крайней мере, подтипу, но позволяет придерживаться простого case с классом String
, так как он окончательный).
public class InitClass {
public static void main(String[] args) {
String str = (str = "hello");
System.out.println(str);
}
}
Мой вопрос: как скомпилировать str = "hello"
? Компилятор уже знает, что str
должен иметь тип String
?
Ответы
Ответ 1
При оценке выражения
Сначала левый операнд оценивается для создания переменной. Если эта оценка завершается внезапно, тогда выражение присваивания внезапно завершается по той же причине; правый операнд не оценивается и не выполняется присвоение.
Это создает переменную str
. Тогда
В противном случае правый операнд оценивается. Если эта оценка завершается внезапно, тогда выражение присваивания резко завершается по той же причине и без присвоения.
В вашем примере правый операнд сам по себе является другим выражением присваивания. Итак, str
, правый операнд оператора присваивания, снова вычисляется для создания переменной str
. Тогда
В противном случае значение правого операнда преобразуется в тип левой переменной, подвергается преобразованию значений (П. 5.1.13) до соответствующего стандартного значения (не , а результат преобразования - хранится в переменной.
Итак "hello"
сохраняется в str
. А так как
Во время выполнения результатом выражения присваивания является значение переменная после назначения. Результат выражение присваивания не является самой переменной.
результатом присвоения "hello"
- str
является значение "hello"
, это значение снова сохраняется в str
.
Ответ 2
Ваш случай эквивалентен
String str;
str = (str = "hello");
Хотя задания смешны, нет ничего неправильного концептуально.
Тем не менее, инициализация переменных, которая ссылается сама по себе, очевидно, не является хорошей идеей. Компилятор попытается помечать его в ситуациях, которые, скорее всего, будут ошибкой программиста; компилятор не делает этого несколько раз; и может также зайти за борт в другое время.
Локальная переменная имеет более строгие требования (чем полевая переменная) - она должна быть назначена первой до использования ее значения. Например, это не будет компилироваться, потому что локальный var читается до его назначения.
String str; // local variable
str = str; // error, try to read `str` before it assigned
Полевая переменная всегда имеет начальное значение по умолчанию; тем не менее, компилятор проверяет очевидные ошибки программиста
int x = x+1; // error. x is field variable.
Но это не катастрофично, если такая проверка завершилась неудачно, поскольку x
имеет значение 0
перед явным назначением
int x;
{ x=x+1; } // ok. x==0, then x==1 after assignment
Однако, если x
является final
, код выше не выполняется, потому что компилятор требует определенного назначения x
сначала перед чтением x
, тем же требованием к локальной переменной. Но эту проверку можно обойти, так как невозможно проанализировать и полностью предотвратить ее полевые переменные
final int x = (this).x+1; // compiles!
В некоторых случаях компилятор выходит за борт, предотвращая использование допустимых случаев использования лямбда
Runnable r1 = ()->System.out.println(r1); // r1 is a field variable
Нет ничего концептуально проблематичного в этом случае; его можно обойти и с помощью (this).
.
Ответ 3
Что первое происходит, так это то, что компилятор идентифицирует тип ссылки, а затем зная, что это строка, которую присваивает "привет" на str.