可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
In method or class scope, the line below compiles (with warning):
int x = x = 1;
In class scope, where variables get their default values, the following gives 'undefined reference' error:
int x = x + 1;
Isn't it the first x = x = 1
should end up with same 'undefined reference' error? Or maybe the second line int x = x + 1
should compile? Or there is something I am missing?
回答1:
tl;dr
For fields, int b = b + 1
is illegal because b
is an illegal forward reference to b
. You can actually fix this by writing int b = this.b + 1
, which compiles without complaints.
For local variables, int d = d + 1
is illegal because d
is not initialized before use. This is not the case for fields, which are always default-initialized.
You can see the difference by attempting to compile
int x = (x = 1) + x;
as a field declaration and as a local variable declaration. The former will fail, but the latter will succeed, because of the difference in semantics.
Introduction
First off, the rules for field and local variable initializers are very different. So this answer will tackle the rules in two parts.
We'll use this test program throughout:
public class test {
int a = a = 1;
int b = b + 1;
public static void Main(String[] args) {
int c = c = 1;
int d = d + 1;
}
}
The declaration of b
is invalid and fails with an illegal forward reference
error.
The declaration of d
is invalid and fails with an variable d might not have been initialized
error.
The fact that these errors are different should hint that the reasons for the errors are also different.
Fields
Field initializers in Java are governed by JLS §8.3.2, Initialization of Fields.
The scope of a field is defined in JLS §6.3, Scope of a Declaration.
Relevant rules are:
- The scope of a declaration of a member
m
declared in or inherited by a class type C (§8.1.6) is the entire body of C, including any nested type declarations.
- Initialization expressions for instance variables may use the simple name of any static variable declared in or inherited by the class, even one whose declaration occurs textually later.
- Use of instance variables whose declarations appear textually after the use is sometimes restricted, even though these instance variables are in scope. See §8.3.2.3 for the precise rules governing forward reference to instance variables.
§8.3.2.3 says:
The declaration of a member needs to appear textually before it is
used only if the member is an instance (respectively static) field of
a class or interface C and all of the following conditions hold:
- The usage occurs in an instance (respectively static) variable initializer of C or in an instance (respectively static) initializer of C.
- The usage is not on the left hand side of an assignment.
- The usage is via a simple name.
- C is the innermost class or interface enclosing the usage.
You can actually refer to fields before they have been declared, except in certain cases. These restrictions are intended to prevent code like
int j = i;
int i = j;
from compiling. The Java spec says "the restrictions above are designed to catch, at compile time, circular or otherwise malformed initializations."
What do these rules actually boil down to?
In short, the rules basically say that you must declare a field in advance of a reference to that field if (a) the reference is in an initializer, (b) the reference is not being assigned to, (c) the reference is a simple name (no qualifiers like this.
) and (d) it is not being accessed from within an inner class. So, a forward reference that satisfies all four conditions is illegal, but a forward reference that fails on at least one condition is OK.
int a = a = 1;
compiles because it violates (b): the reference a
is being assigned to, so it's legal to refer to a
in advance of a
's complete declaration.
int b = this.b + 1
also compiles because it violates (c): the reference this.b
is not a simple name (it's qualified with this.
). This odd construct is still perfectly well-defined, because this.b
has the value zero.
So, basically, the restrictions on field references within initializers prevent int a = a + 1
from being successfully compiled.
Observe that the field declaration int b = (b = 1) + b
will fail to compile, because the final b
is still an illegal forward reference.
Local variables
Local variable declarations are governed by JLS §14.4, Local Variable Declaration Statements.
The scope of a local variable is defined in JLS §6.3, Scope of a Declaration:
- The scope of a local variable declaration in a block (§14.4) is the rest of the block in which the declaration appears, starting with its own initializer and including any further declarators to the right in the local variable declaration statement.
Note that initializers are within the scope of the variable being declared. So why doesn't int d = d + 1;
compile?
The reason is due to Java's rule on definite assignment (JLS §16). Definite assignment basically says that every access to a local variable must have a preceding assignment to that variable, and the Java compiler checks loops and branches to ensure that assignment always occurs prior to any use (this is why definite assignment has an entire specification section dedicated to it). The basic rule is:
- For every access of a local variable or blank final field
x
, x
must be definitely assigned before the access, or a compile-time error occurs.
In int d = d + 1;
, the access to d
is resolved to the local variable fine, but since d
has not been assigned before d
is accessed, the compiler issues an error. In int c = c = 1
, c = 1
happens first, which assigns c
, and then c
is initialized to the result of that assignment (which is 1).
Note that because of definite assignment rules, the local variable declaration int d = (d = 1) + d;
will compile successfully (unlike the field declaration int b = (b = 1) + b
), because d
is definitely assigned by the time the final d
is reached.
回答2:
int x = x = 1;
is equivalent to
int x = 1;
x = x; //warning here
while in
int x = x + 1;
first we need to compute x+1
but the value of x is not known so you get an error (the compiler knows that the value of x is not known)
回答3:
It's roughly equivalent to:
int x;
x = 1;
x = 1;
Firstly, int <var> = <expression>;
is always equivalent to
int <var>;
<var> = <expression>;
In this case, your expression is x = 1
, which is also a statement. x = 1
is a valid statement, since the var x
has already been declared. It is also an expression with the value 1, which is then assigned to x
again.
回答4:
In java or in any modern language, assignment comes from the right.
Suppose if you are having two variables x and y,
int z = x = y = 5;
This statement is valid and this is how the compiler splits them.
y = 5;
x = y;
z = x; // which will be 5
But in your case
int x = x + 1;
The compiler gave an exception because, it splits like this.
x = 1; // oops, it isn't declared because assignment comes from the right.
回答5:
int x = x = 1;
is not equal to:
int x;
x = 1;
x = x;
javap helps us again, these are JVM instruction generated for this code:
0: iconst_1 //load constant to stack
1: dup //duplicate it
2: istore_1 //set x to constant
3: istore_1 //set x to constant
more like:
int x = 1;
x = 1;
Here is no reason to throw undefined reference error. There is now usage of variable prior to it's initialization, so this code fully comply with specification. In fact there is no usage of variable at all, just assignments. And JIT compiler will go even further, it will eliminate such constructions. Saying honestly, I don't understand how this code is connected to JLS's specification of variable initialization and usage. No usage no problems. ;)
Please correct if I'am wrong. I cant figure out why other answers, which refer to many JLS paragraphs collect so many pluses. These paragraphs has nothing in common with this case. Just two serial assignments and no more.
If we write:
int b, c, d, e, f;
int a = b = c = d = e = f = 5;
is equal to:
f = 5
e = 5
d = 5
c = 5
b = 5
a = 5
Right most expression is just assigned to variables one by one, without any recursion. We can mess variables any way we like :
a = b = c = f = e = d = a = a = a = a = a = e = f = 5;
回答6:
In int x = x + 1;
you add 1 to x , so what is the value of x
, it's not created yet.
But in int x=x=1;
will compile with no error because you assign 1 to x
.
回答7:
Your first piece of code contains a second =
instead of a plus. This will compile anywhere while the second piece of code will not compile in either place.
回答8:
In the second piece of code, x is used before its declaration, while in the the first piece of code it is just assigned twice which doesn't make sense but is valid.
回答9:
Let's break it down step by step, right associative
int x = x = 1
x = 1
, assign 1 to a variable x
int x = x
, assign what x is to itself, as an int. Since x was previously assigned as 1, it retains 1, albeit in a redundant fashion.
That compiles fine.
int x = x + 1
x + 1
, add one to a variable x. However, x being undefined this will cause a compile error.
int x = x + 1
, thus this line compile errors as the right portion of the equals will not compile adding one to an unassigned variable
回答10:
The second one int x=x=1
is compile because you are assigning the value to the x but in other case int x=x+1
here the variable x is not initialized , Remember in java local variable are not initialized to default value.
Note If it's (int x=x+1
) in class scope also then also it will give compilation error as the variable is not created.
回答11:
int x = x + 1;
compiles successfully in Visual Studio 2008 with warning
warning C4700: uninitialized local variable 'x' used`
回答12:
x is not initialised in x = x + 1
;.
The Java programming language is statically-typed, which means that all variables must first be declared before they can be used.
See primitive data types
回答13:
The line of code does not compile with a warning because of how the code actually works.
When you run the code int x = x = 1
, Java first creates the variable x
, as defined. Then it runs the assignment code (x = 1
). Since x
is already defined, the system has no errors setting x
to 1. This returns the value 1, because that is now the value of x
. Therefor, x
is now finally set as 1.
Java basically executes the code as if it was this:
int x;
x = (x = 1); // (x = 1) returns 1 so there is no error
However, in your second piece of code, int x = x + 1
, the + 1
statement requires x
to be defined, which by then it is not. Since assignment statements always mean the code to the right of the =
is run first, the code will fail because x
is undefined. Java would run the code like this:
int x;
x = x + 1; // this line causes the error because `x` is undefined
回答14:
Complier read statements from right to left and we designed to do the opposite. That's why it annoyed at first. Make this a habbit to read statements (code) from right to left you won't have such problem.