How does this compiles without error? As my understanding, the compiler checks the type of the variable (in this case String
), then sees if the type of the expression on the right side corresponds to the variable's type (or at least a subtype but let's stick to the simple case with the String
class since it's final).
public class InitClass {
public static void main(String[] args) {
String str = (str = "hello");
System.out.println(str);
}
}
My question is how does str = "hello"
compile? Is the compiler already aware that str
should be of type String
?
When evaluating an assignment expression
First, the left-hand operand is evaluated to produce a variable. If this evaluation completes abruptly, then the assignment expression completes abruptly for the same reason; the right-hand operand is not evaluated and no assignment occurs.
That produces the variable str
. Then
Otherwise, the right-hand operand is evaluated. If this evaluation completes abruptly, then the assignment expression completes abruptly for the same reason and no assignment occurs.
In your example, the right hand operand is itself another assignment expression. So str
, the right hand operand of the assignment operator, is again evaluated to produce a variable, str
. Then
Otherwise, the value of the right-hand operand is converted to the type of the left-hand variable, is subjected to value set conversion (§5.1.13) to the appropriate standard value set (not an extended-exponent value set), and the result of the conversion is stored into the variable.
So "hello"
is stored into str
. And since
At run time, the result of the assignment expression is the value of the variable after the assignment has occurred. The result of an assignment expression is not itself a variable.
the result of the assignment of "hello"
to str
is the value "hello"
, that value is again stored in str
.
Your case is equivalent to
String str;
str = (str = "hello");
Although the assignments are funny looking, there's nothing wrong conceptually.
Nevertheless, a variable initialization that references itself is obviously not a good idea. The compiler will try to flag it in situations that's very likely a programer error; the compiler fails to do so some times; and may also go overboard some other times.
A local variable has a stricter requirement (than a field variable) - it must be assigned first before its value is used. For example, this won't compile, because the local var is read before it's assigned.
String str; // local variable
str = str; // error, try to read `str` before it's assigned
A field variable always has a default initial value; nevertheless, the compiler checks for apparent programer mistakes
int x = x+1; // error. x is field variable.
But it's not catastrophic if such checking fails, since x
has a value 0
before explicit assignment
int x;
{ x=x+1; } // ok. x==0, then x==1 after assignment
However, if x
is final
, the code above fails, because compiler requires definite assignment of x
first before reading x
, the same requirement for a local variable. But this checking can be circumvented, because it's impossible to analyze and prevent it fully for field variables
final int x = (this).x+1; // compiles!
In some cases, the compiler goes overboard, preventing legit use cases involving lambda
Runnable r1 = ()->System.out.println(r1); // r1 is a field variable
There's nothing conceptually problematic in this use case; it can be circumvented by (this).
too.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With