Take a look at below code:
class Foo{
public static int x = 1;
}
class Bar{
public static void main(String[] args) {
Foo foo;
System.out.println(foo.x); // Error: Variable 'foo' might not have been initialized
}
}
As you see while trying to access static field x via an uninitialized local variable Foo foo; code foo.x generates compilation error: Variable 'foo' might not have been initialized.
It could seem like this error makes sense, but only until we realize that to access a static member the JVM doesn't actually use the value of a variable, but only its type.
For instance I can initialize foo with value null and this will let us access x without any problems:
Foo foo = null;
System.out.println(foo.x); //compiles and at runtime prints 1!!!
Such scenario works because compiler realizes that x is static and treats foo.x as if it was written like Foo.x (at least that is what I thought until now).
So why compiler suddenly insists on foo having a value which it will NOT use at all?
Disclaimer: This is not code which would be used in real application, but interesting phenomenon which I couldn't find answer to on Stack Overflow, so I decided to ask about it.
§15.11. Field Access Expressions:
If the field is static:
The Primary expression is evaluated, and the result is discarded. If evaluation of the Primary expression completes abruptly, the field access expression completes abruptly for the same reason.
Where earlier it states that field access is identified by Primary.Identifier.
This shows that even though it seems to not use the Primary, it is still evaluated and the result is then discarded which is why it will need to be initialized. This can make a difference when the evaluation halts the access as stated in the quote.
EDIT:
Here is a short example just to demonstrate visually that the Primary is evaluated even though the result is discarded:
class Foo {
public static int x = 1;
public static Foo dummyFoo() throws InterruptedException {
Thread.sleep(5000);
return null;
}
public static void main(String[] args) throws InterruptedException {
System.out.println(dummyFoo().x);
System.out.println(Foo.x);
}
}
Here you can see that dummyFoo() is still evaluated because the print is delayed by the 5 second Thread.sleep() even though it always returns a null value which is discarded.
If the expression was not evaluated the print would appear instantly, which can be seen when the class Foo is used directly to access x with Foo.x.
Note: Method invocation is also considered a Primary shown in §15.8 Primary Expressions.
Chapter 16. Definite Assignment
Each local variable (§14.4) and every blank final field (§4.12.4, §8.3.1.2) must have a definitely assigned value when any access of its value occurs.
It doesn't really matter what you try to access via a local variable. The rule is that it should be definitely assigned before that.
To evaluate a field access expression foo.x, the primary part of it (foo) must be evaluated first. It means that access to foo will occur, which will result in a compile-time error.
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.
There is value in keeping the rules as simple as possible, and “don’t use a variable that might not have been initialised” is as simple as it gets.
More to the point, there is an established way of calling static methods - always use the class name, not a variable.
System.out.println(Foo.x);
The variable “foo” is unwanted overhead that should be removed, and the compiler errors and warnings could be seen as helping leading towards that.
Other answers perfectly explain the mechanism behind what is happening. Maybe you also wanted the rationale behind Java's specification. Not being a Java expert, I cannot give you the original reasons, but let me point this out:
Every piece of code either has a meaning or it triggers a compilation error.
(For statics, because an instance is unnecessary, Foo.x is natural.)
Now, what shall we do with foo.x (access through instance variable)?
It could be a compilation error, as in C#, or
It has a meaning. Because Foo.x already means "simply access x", it is reasonable that the expression foo.x has a different meaning; that is, every part of the expression is valid and access x.
Let's hope someone knowledgeable can tell the real reason. :-)
Related
Consider the following code snippet in Java. It won't compile.
package temppkg;
final public class Main
{
private String x;
private int y;
private void show()
{
String z;
int a;
System.out.println(x.toString()); // Causes a NullPointerException but doesn't issue a compiler error.
System.out.println(y); // Works fine displaying its default value which is zero.
System.out.println(z.toString()); // Causes a compile-time error - variable z might not have been initialized.
System.out.println(a); // Causes a compile-time error - variable a might not have been initialized.
}
public static void main(String []args)
{
new Main().show();
}
}
Why do the class members (x and y) declared in the above code snippet not issue any compile-time error even though they are not explicitly initialized and only local variables are required to be initialized?
When in doubt, check the Java Language Specification (JLS).
In the introduction you'll find:
Chapter 16 describes the precise way in which the language ensures
that local variables are definitely set before use. While all other
variables are automatically initialized to a default value, the Java
programming language does not automatically initialize local variables
in order to avoid masking programming errors.
The first paragraph of chapter 16 states,
Each local variable and every blank final field must have a definitely
assigned value when any access of its value occurs....A Java compiler
must carry out a specific conservative flow analysis to make sure
that, for every access of a local variable or blank final field f, f
is definitely assigned before the access; otherwise a compile-time
error must occur.
The default values themselves are in section 4.12.5. The section opens with:
Each class variable, instance variable, or array component is
initialized with a default value when it is created.
...and then goes on to list all the default values.
The JLS is really not that hard to understand and I've found myself using it more and more to understand why Java does what it does...after all, it's the Java bible!
Why would they issue a compile warning?, as instance variables String will get a default value of null, and int will get a default value of 0.
The compiler has no way to know that x.toString(), will cause a runtime exception, because the value of null is not actually set till after runtime.
In general the compiler couldn't know for sure if a class member has or has not been initialized before. For example, you could have a setter method that sets the value for a class member, and another method which accesses that member. The compiler can't issue a warning when accessing that variable because it can't know whether the setter has been called before or not.
I agree that in this case (the member is private and there is no single method that writes the variable) it seems it could raise a warning from the compiler. Well, in fact you are still not sure that the variable has not been initialized since it could have been accessed via reflexion.
Thins are much easier with local variables, since they can't be accessed from outside the method, nor even via reflexion (afaik, please correct me if wrong), so the compiler can be a bit more helpful and warn us of uninitialized variables.
I hope this answer helps you :)
Class members could have been initialized elsewhere in your code, so the compiler can't see if they were initialized at compilation time.
Member variables are automatically initialized to their default values when you construct (instantiate) an object. That holds true even when you have manually initialized them, they will be initialized to default values first and then to the values you supplied.
This is a little little lengthy article but it explains it: Object initialization in Java
Whereas for the local variables (ones that are declared inside a method) are not initialized automatically, which means you have to do it manually, even if you want them to have their default values.
You can see what the default values are for variables with different data types here.
The default value for the reference type variables is null. That's why it's throwing NullPointerException on the following:
System.out.println(x.toString()); // Causes a NullPointerException but doesn't issue a compiler error.
In the following case, the compiler is smart enough to know that the variable is not initialized yet (because it's local and you haven't initialized it), that's why compilation issue:
System.out.println(z.toString()); // "Cuases a compile-time error - variable z might not have been initialized.
I'm indebted to this answer for the idea.
The following code compiles, but certainly shouldn't. It uses the value of x before it's been initialised. If you remove the StaticAssign. qualifier then it no longer compiles.
public class StaticAssign {
public static final int x;
static {
System.out.println(StaticAssign.x);
x = 5;
}
public static void main(String[] args) {}
}
On my machine, this consistently prints 0. If I change the type of x to String, it consistently prints null.
The question is: will it definitely print 0 or null, or might it access uninitialised memory and print out something else?
And even if this code gets through the compiler, why isn't it picked up by the JVM?
Is there a way to do nasty things based on this?
It actually has been initialized. Variables in the global scope are initialized automatically. Variables of Object type will be initialized to null primitive like int will be initialized to 0. A variable declared not in the global scope must be initialized ie. declared in a method. Another problem is declaring it as final this is telling the compiler it must be explicitly initialized. So by adding the x=5 you are bypassing the compiler error saying it must be explicitly initialized. When you access it before this line at run-time it is initialized to 0 like any other primitive int type variable in the global scope.
This is due to the way in which the classes are loaded.
First the class StaticAssign definition is loaded and the fields are initialized to default values:
StaticAssign.x = 0;
Then the initalization block is executed.
System.out.println(StaticAssign.x);
x = 5;
There is a reference to StaticAssign.x, the current class. As recursive initialization attempts are simply ignored, value of x is still 0.
This means that:
System.out.println(StaticAssign.x)
is valid because StaticAssign.x is a reference to field of an allready loaded class.
But if yo do:
System.out.println(x)
then x is a reference to a final uninitalized field.
On the other hand, you never will access uninitialised memory. When the class definition is loaded variables are initialized to default values, before the initalization block is executed.
EDIT:
There is a nice video "Elvis Lives Again" from Java Puzzlers that shows this much more better than I can explain
Why isn't it possible to access static final fields from a corresponding static initializer using the declaring class as qualifier (in a static way)?
At first, I thought this was an Eclipse bug:
I also consisted some lack of knowledge, because static initializers are not my everyday business. But lo and behold, this works without the class qualifier as expected:
To complete my test series, I gave it a try in the bash:
causing the same result.
This leads me to the final question:
Is there any reason to prohibit the class qualifier when accessing static final fields from static initializer blocks?
Because the declaring class wasn't initialized before?
Actually, you can initialize a static field from a static initializer.
But I think (I'm not sure), you are having another problem. The problem here is that you're (according to the compiler) trying to assign a final field. However, your true intention is not to assign it.. you're trying to initialize it. But the compiler doesn't get this.
When you call something like Test.I the compiler will think you are trying to modify a static variable that it may be previously initialized (i.e. from a static initializer). The compiler is not that smart to see that you're actually initializing your variable, it's just interpreting you're assigning a static variable from a class, be it Test or be it Foo.
However, if you call it without the class qualifier, the compiler knows that you're trying to modify your own static variable, and in a static initializer, so the operation is safe for the final field.
Please, I hope I'm being clear enough, and please, be noted that I'm not sure about this behaviour.
In order to initialise a final variable in a initialization block, the simple name of the variable should be use. i.e. the variable name alone with out any qualifiers.
It is stated on the java language specification as below
"Similarly, every blank final variable must be assigned at most once; it must be definitely unassigned when an assignment to it occurs. Such an assignment is defined to occur if and only if either the simple name of the variable, or its simple name qualified by this, occurs on the left hand side of an assignment operator. A Java compiler must carry out a specific conservative flow analysis to make sure that, for every assignment to a blank final variable, the variable is definitely unassigned before the assignment; otherwise a compile-time error must occur."
I have this code
private static Set<String> myField;
static {
myField = new HashSet<String>();
myField.add("test");
}
and it works. But when I flip the order, I get an illegal forward reference error.
static {
myField = new HashSet<String>();
myField.add("test"); // illegal forward reference
}
private static Set<String> myField;
I'm a little bit shocked, I didn't expect something like this from Java. :)
What happens here? Why is the order of declarations important? Why does the assignment work but not the method call?
First of all, let's discuss what a "forward reference" is and why it is bad. A forward reference is a reference to a variable that has not yet been initialized, and it is not confined only to static initalizers. These are bad simply because, if allowed, they'd give us unexpected results. Take a look at this bit of code:
public class ForwardRef {
int a = b; // <--- Illegal forward reference
int b = 10;
}
What should j be when this class is initialized? When a class is initialized, initializations are executed in order the first to the last encountered. Therefore, you'd expect the line
a = b;
to execute prior to:
b = 10;
In order to avoid this kind of problems, Java designers completely disallowed such uses of forward references.
EDIT
this behaviour is specified by section 8.3.2.3 of Java Language Specifications:
The declaration of a member needs to appear 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.
C is the innermost class or interface enclosing the usage.
A compile-time error occurs if any of the three requirements above are not met.
try this:
class YourClass {
static {
myField = new HashSet<String>();
YourClass.myField.add("test");
}
private static Set<String> myField;
}
it should compile without errors according the JLS...
(don't really help, or?)
In Java, all initializers, static or otherwise, are evaluated in the order in which they appear in the class definition.
See the rules for forward references in the JLS. You cannot use forward references if:
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.
Since all of these hold for your example, the forward reference is illegal.
To elaborate on DFA's answer:
I think what's tripping you up is the "left hand side" rule in the second bullet point in JLS 8.2.3.2. In your initialization, myField is on the left-hand side. In your call to add, it's on the right-hand side. The code here is implicitly:
boolean result = myField.add('test')
You're not evaluating the result, but the compiler still acts as if it's there. That's why your initialization passes while your call to add() fails.
As for why this is so, I have no idea. It may well be for the convenience of the JVM developers, for all I know.
I think the method call is problematic because the compiler cannot determine which add() method to use without a reference type for myField.
At runtime, the method used will be determined by the object type, but the compiler only knows about the reference type.
Consider this:
public class TestClass {
private String a;
private String b;
public TestClass()
{
a = "initialized";
}
public void doSomething()
{
String c;
a.notify(); // This is fine
b.notify(); // This is fine - but will end in an exception
c.notify(); // "Local variable c may not have been initialised"
}
}
I don't get it. "b" is never initialized but will give the same run-time error as "c", which is a compile-time error. Why the difference between local variables and members?
Edit: making the members private was my initial intention, and the question still stands...
The language defines it this way.
Instance variables of object type default to being initialized to null.
Local variables of object type are not initialized by default and it's a compile time error to access an undefined variable.
See section 4.12.5 for SE7 (same section still as of SE14)
http://docs.oracle.com/javase/specs/jls/se7/html/jls-4.html#jls-4.12.5
Here's the deal. When you call
TestClass tc = new TestClass();
the new command performs four important tasks:
Allocates memory on the heap for the new object.
Initiates the class fields to their default values (numerics to 0, boolean to false, objects to null).
Calls the constructor (which may re-initiate the fields, or may not).
Returns a reference to the new object.
So your fields 'a' and 'b' are both initiated to null, and 'a' is re-initiated in the constructor. This process is not relevant for method calling, so local variable 'c' is never initialized.
For the gravely insomniac, read this.
The rules for definite assignment are quite difficult (read chapter 16 of JLS 3rd Ed). It's not practical to enforce definite assignment on fields. As it stands, it's even possible to observe final fields before they are initialised.
The compiler can figure out that c will never be set. The b variable could be set by someone else after the constructor is called, but before doSomething(). Make b private and the compiler may be able to help.
The compiler can tell from the code for doSomething() that c is declared there and never initialized. Because it is local, there is no possibility that it is initialized elsewhere.
It can't tell when or where you are going to call doSomething(). b is a public member. It is entirely possible that you would initialize it in other code before calling the method.
Member-variables are initialized to null or to their default primitive values, if they are primitives.
Local variables are UNDEFINED and are not initialized and you are responsible for setting the initial value. The compiler prevents you from using them.
Therefore, b is initialized when the class TestClass is instantiated while c is undefined.
Note: null is different from undefined.
You've actually identified one of the bigger holes in Java's system of generally attempting to find errors at edit/compile time rather than run time because--as the accepted answer said--it's difficult to tell if b is initialized or not.
There are a few patterns to work around this flaw. First is "Final by default". If your members were final, you would have to fill them in with the constructor--and it would use path-analysis to ensure that every possible path fills in the finals (You could still assign it "Null" which would defeat the purpose but at least you would be forced to recognize that you were doing it intentionally).
A second approach is strict null checking. You can turn it on in eclipse settings either by project or in default properties. I believe it would force you to null-check your b.notify() before you call it. This can quickly get out of hand so it tends to go with a set of annotations to make things simpler:
The annotations might have different names but in concept once you turn on strict null checking and the annotations the types of variables are "nullable" and "NotNull". If you try to place a Nullable into a not-null variable you must check it for null first. Parameters and return types are also annotated so you don't have to check for null every single time you assign to a not-null variable.
There is also a "NotNullByDefault" package level annotation that will make the editor assume that no variable can ever have a null value unless you tag it Nullable.
These annotations mostly apply at the editor level--You can turn them on within eclipse and probably other editors--which is why they aren't necessarily standardized. (At least last time I check, Java 8 might have some annotations I haven't found yet)