In method or class scope, the line below compiles (with warning):
int x = x = 1;
In class scope, where variables get their default valu
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
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.
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
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.
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.
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.
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.
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:
m
declared in or inherited by a class type C (§8.1.6) is the entire body of C, including any nested type declarations.§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 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:
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:
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.