A static field cannot be referenced before it is defined or initialized:
static Integer j = i; /* compile error */
static final Integer i = 5;
However, when it is referenced from an instance initialization block (in an anonymous inner class), not even a warning is generated.
See example:
class StaticInitialization {
static final Object o = new Object() {{
j = i;
}};
static Integer j, k;
static final Integer i = 5;
static final Object o2 = new Object() {{
k = i;
}};
}
The result is: j == null
, k == 5
, so clearly we've made a reference, order matters, and no warning or compilation error.
Is this code legal?
Yes, that happens because first the space for all variables is allocated (and initialized with #0) and after that all the initialization is done in the order of your code.
Means, if you change the code like this:
The result is for all
variables == 5
.Is this code legal? Probably. I don't think it's the compiler's job to analyze your deliberate side-effects of object instantiation in buggering up static variables.
The limited analysis of 'declare before referencing' from other statics in the same class is really just a helper against the most common boo-boos, not a ironclad guarantee against indirect errors.
I'm really not at all surprised that the the "declare before referencing" analysis is limited in scope to direct access of static variables in other
static
declarations. This is a simple & compact analysis with minimal complexity & very fast.Extending it to consider side-effects of object instantiation & method calls, OTOH, would require the 20-1000x greater weight & scope of static program analysis. Static analysis requires access to potentially the entire compiled program code, with constraint-based computation to determine what may possibly happen, and run-times potentially in the minutes.
Given these choices it is fairly easy, in the Java language designer's shoes, to choose simple analysis covering only direct accesses from fields within the same class.
Your code is similar to this one:
When you refer to
i
insideFoo1
,i
was null. However,i
becomes5
when you refer to it insideFoo2
. Note that ifi
was a compile-constant (i
isint
notInteger
), thenj
would be 5.See this related question: Creating an object in a static way
The rules for
final
variables are quite different from all others. For example, forfinal
variables the compiler must check if the variable has definitely been assigned once and not been assigned afterwards.This is possible, because there are quite a few restrictions on
final
variables (which is pretty much the point offinal
).Therefore, the in-depth analysis of "is this assigned before use" only really works within the limited world of final variables.
In the first block, you're not referencing a static field before it is initialized, but referencing before it is defined (as the compiler will tell you). This will work, for example:
All fields have default values when not set explicitly (for objects, that's the
null
reference, for primitives, that's the corresponding sensible default,0
,false
, etc.). Therefore, the fields are always initialized so the compiler doesn't need to check for that.Regarding
final
: it's really a modifier used for the benefit of the developer(s). As the JSL states,final
fields must be "definitely assigned" before access. That does not mean they would have no value otherwise (if it were notfinal
), it just means that the compiler protects you against not assigning it explicitly, if it cannot find that assignment.So, for non-
final
fields, you most definitely can reference them before an explicit assignment. Just paste the code snippet above to verify.This is defined in JLS 8.3.2.3.
That is why you are getting error when you do this..
But accesses by methods are not checked in the same way.