java enum illegal forward reference with constant [duplicate] - java

In the below code while accessing a static variable with class name it doesn't throw a forward reference error but accessing it without class name does.
Why this doesn't happen when accessing with class name?
class Test{
static {
System.out.println(a); // shows error
a = 99; // and this line too doesn't give error
System.out.println(Test.a); // this line doesn't
}
static int a = 10;
static{
System.out.println(a);
}
}

The rules for forward reference is defined in JLS §8.3.3:
Use of class variables whose declarations appear textually after the
use is sometimes restricted, even though these class variables are in
scope (§6.3). Specifically, it is a compile-time error if all of the
following are true:
The declaration of a class variable in a class or interface C appears
textually after a use of the class variable;
The use is a simple name in either a class variable initializer of C
or a static initializer of C;
The use is not on the left hand side of an assignment;
C is the innermost class or interface enclosing the use.
So, basically your first Sysout(), satisfies all the above 4 conditions, and hence it's a compile time error.
In the 2nd Sysout(), you're accessing a using it's qualified name, rather than simple name, which as per the above rules is allowed.
Now, the reason for this would be, when you access Test.a, the compiler is sure that Test class has been loaded and all static fields have been initialized, so it can access the field a. But while accessing a on simple name, the compiler isn't sure that initializer for a has already run or not, since it might still be in process of loading the class.
Consider the following process of loading of class:
When a class is loaded, memory is allocated for all the static variables declared in it. At this point, the variable a has got its memory allocated (declaration done)
Then all the static initializers run in the order of occurrence.
First statement is Sysout(a);. a hasn't been initialized yet, so you can't access it. (error)
Second statement is a = 99. Here you're actually initializing the variable a. Perfectly fine.
Third one is Sysout(Test.a) - reasoning for this is already posted above. Compiler knows Test is already loaded.
Then static int a = 10 is executed. It re-initializes a to 10. Remember, declaration part is already taken care of in first step.

First , let's see what JLS have to say for illegal forward references.
Use of class variables whose declarations appear textually after the
use is sometimes restricted, even though these class variables are in
scope (§6.3). Specifically, it is a compile-time error if all of the
following are true:
The declaration of a class variable in a class or interface C appears
textually after a use of the class variable;
The use is a simple name in either a class variable initializer of C
or a static initializer of C;
The use is not on the left hand side of an assignment;
C is the innermost class or interface enclosing the use.
Use of instance variables whose declarations appear textually after
the use is sometimes restricted, even though these instance variables
are in scope. Specifically, it is a compile-time error if all of the
following are true:
The declaration of an instance variable in a class or interface C
appears textually after a use of the instance variable;
The use is a simple name in either an instance variable initializer of
C or an instance initializer of C;
The use is not on the left hand side of an assignment;
C is the innermost class or interface enclosing the use.
It defines what is a illegal forward reference for each - static and instance variables. However, definition seems to be same for both of them. Since your question asks about static, we will dig deep into this only.
1) See, for a static variable, there is declaration and there is initialization.
static int a; //only declaration
static int b = 10; //both declaration and initialization
2) Use of class variables whose declarations appear textually after the use is sometimes restricted, even though these class variables are in scope.
What does it explains to us ? It says that there are cases when we can use static variables even if we declare them afterwards. And in some cases this wont be allowed. So, what are the cases that this won't be allowed ?
If the following 4 conditions are true at the same time. Else you are free to use it even though you have declared it afterwards.
a) The declaration of a class variable in a class or interface C appears textually after a use of the class variable;
b) The use is a simple name in either a class variable initializer of C or a static initializer of C;
c) The use is not on the left hand side of an assignment;
d) C is the innermost class or interface enclosing the use.
Well, the a) point is simple. It says that you must be looking at these rules only when you want to use a static variable before declaring it else why would you dig up into this JLS.
The b) point is that if you use simple name like boy [and not class name appended to it like MyClass.boy only then you might enter the problem of illegal forward reference else you are safe my friend. But just this condition don't qualifies for Illegal forward reference else your a=99 in you code would have given us error immediately. There are 2 more conditions that HAVE to be true for this error to generate . If the below 2 does not qualify, you are allowed to use it like this.]
The point c) is pretty straightforward. Are you not using in left hand side of assignment ? If we look at a=99 , Nope !! System.out.println(a) - This is not even an assignment. Hence no left hand assignment is true.
The point d) is also simple. It just tell you which class/Interface C he means in definition.Your C = Test.
Now let's revisit your code. At each line of code, I will comment True+False+True+false something like this meaning that for each line what point a), b) , c) ,d) will give to each of line. Okay ?
class Test {
static {
System.out.println(a); // True + True + True +True ; Illegal forward reference
a = 99; // True + True + False +True ; No illegal forward reference
System.out.println(Test.a); // True + False + True + True No illegal forward reference
}
static int a = 10;
static {
System.out.println(a);
}
}
The next question one may think is now what it will print ? What values will it take if we use before declaring it ?
Now we come to rules of static initialization. Here, we will go according to your code.
When class is loaded and no illegal forward reference is not there , all the static variables have been initialized to default value and are present in memory.
Now, after this static initialization takes place.
class Test {
static {
System.out.println(Test.a); // prints 0
a = 99; // a get 99
System.out.println(Test.a); // prints 99
}
static int a = 10;
static {
System.out.println(a); // prints 10
}
public static void main(String[] args) {
}
}

Well, the static block
static {
System.out.println(a);
a = 99;
System.out.println(Test.a);
}
will be executed before a is declared.
The line with Test.a won't fire any error because the compiler checks and finds static variable a declared within class Test.

Class initializers are run at the time that the class is loaded, and you cannot determine when exactly this happens. Code that runs within a class initializer should only be for things that need to be processed or "initialized" for other static methods (or the class itself) to work properly.
Additionally, you are referencing a variable that has not yet been declared. In your class initializer (the first static block) you assign a = 99, but a has not yet been declared. If anything you'd want to declare the variable, and then initialize it within the static block.
There is no reason to use class initializers for the type of code you are posting. This should be a static method if anything.
Here's an example
class Test{
static int a = 10;
static void doSomething(){
System.out.println(a);
a = 99;
System.out.println(a);
}
}
Then in main you can call Test.doSomething();

Related

Discrepancy in "rules" with variables with same name [duplicate]

I am learning Java and I understand that you cannot name a variable declared within an inner scope the same name as a variable declared in an outer scope as shown below
public class Practice {
public static void main(String[] args){
int x = 10;
if (x == 10){
int x = 10;
}
}
}
However, I noticed that the following is not illegal
public class Practice {
int x = 10;
public static void main(String[] args){
int x = 10;
if (x == 10) {
}
}
}
Is this not a variable that is declared twice??
Is this not a variable that is declared twice??
No, it is not. Because they both are in different scope. x outside of main function has class level scope while x inside of main has method/function level scope.
It is legal for 2 variables in different scope to have same name.
Please DO read §6.3. Scope of a Declaration from JLS. Below are few of the statement from that section.
The scope of a declaration is the region of the program within which
the entity declared by the declaration can be referred to using a
simple name, provided it is visible (§6.4.1).
A declaration is said to be in scope at a particular point in a
program if and only if the declaration's scope includes that point.
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.
There are lot of concepts related to scope like shadowing, so do read §6.4. Shadowing and Obscuring.
JLS is the best place to learn what is allowed and what is not allowed in Java. Feel free to read sections there.
In the second case when x is declared outside the method, it is an instance variable of the class Practice. Within the method, scope of x(declared inside main) is limited to the main method.
In the first case when you declare x within the method, you cannot declare it inside if because it is already declared in the scope of the method outside if.
The first code snippets shows the variables are in method's scope. So two variable are belonging to same method which is not allowed.
But in the second example one variable is on instance level and the other one in method's scope, so there is no duplicate. In the second example if you want to access the instance variable, you have to do it using this.x.
Check more on scope and lifetime of variables here
http://www.javawithus.com/tutorial/scope-and-lifetime-of-variables
Why can you have a duplicate variable name in java for a variable outside of a method?
It is not illegal because it is allowed by the Java Language Specification. (Yes ... circular explanation)
As with all such questions, we cannot tell you for sure why it is allowed, because the Java language was designed behind closed doors. However, a couple of possible reasons are:
Java was originally designed to be "palatable" to C and C++ programmers, and this is a case where doing it differently (banning shadowing) would have caused objections.
A lot of people actually like it that they don't have to think of different names for variables; e.g.
public class Foo {
private Bar bar;
public Foo() {
Bar bar = new Bar();
this.bar = bar;
}
}
Admittedly, there is a downside. Sometimes, people mistakenly declare local variables that shadow instance (or static) variables. Particularly newbies :-)
Is this not a variable that is declared twice??
No. It is actually two >>distinct<< variables. Both exist. Within the method, the local declaration of x shadows the instance declaration of x. But you can still refer to the latter as this.x.

Why can you have a duplicate variable name in java for a variable outside of a method?

I am learning Java and I understand that you cannot name a variable declared within an inner scope the same name as a variable declared in an outer scope as shown below
public class Practice {
public static void main(String[] args){
int x = 10;
if (x == 10){
int x = 10;
}
}
}
However, I noticed that the following is not illegal
public class Practice {
int x = 10;
public static void main(String[] args){
int x = 10;
if (x == 10) {
}
}
}
Is this not a variable that is declared twice??
Is this not a variable that is declared twice??
No, it is not. Because they both are in different scope. x outside of main function has class level scope while x inside of main has method/function level scope.
It is legal for 2 variables in different scope to have same name.
Please DO read §6.3. Scope of a Declaration from JLS. Below are few of the statement from that section.
The scope of a declaration is the region of the program within which
the entity declared by the declaration can be referred to using a
simple name, provided it is visible (§6.4.1).
A declaration is said to be in scope at a particular point in a
program if and only if the declaration's scope includes that point.
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.
There are lot of concepts related to scope like shadowing, so do read §6.4. Shadowing and Obscuring.
JLS is the best place to learn what is allowed and what is not allowed in Java. Feel free to read sections there.
In the second case when x is declared outside the method, it is an instance variable of the class Practice. Within the method, scope of x(declared inside main) is limited to the main method.
In the first case when you declare x within the method, you cannot declare it inside if because it is already declared in the scope of the method outside if.
The first code snippets shows the variables are in method's scope. So two variable are belonging to same method which is not allowed.
But in the second example one variable is on instance level and the other one in method's scope, so there is no duplicate. In the second example if you want to access the instance variable, you have to do it using this.x.
Check more on scope and lifetime of variables here
http://www.javawithus.com/tutorial/scope-and-lifetime-of-variables
Why can you have a duplicate variable name in java for a variable outside of a method?
It is not illegal because it is allowed by the Java Language Specification. (Yes ... circular explanation)
As with all such questions, we cannot tell you for sure why it is allowed, because the Java language was designed behind closed doors. However, a couple of possible reasons are:
Java was originally designed to be "palatable" to C and C++ programmers, and this is a case where doing it differently (banning shadowing) would have caused objections.
A lot of people actually like it that they don't have to think of different names for variables; e.g.
public class Foo {
private Bar bar;
public Foo() {
Bar bar = new Bar();
this.bar = bar;
}
}
Admittedly, there is a downside. Sometimes, people mistakenly declare local variables that shadow instance (or static) variables. Particularly newbies :-)
Is this not a variable that is declared twice??
No. It is actually two >>distinct<< variables. Both exist. Within the method, the local declaration of x shadows the instance declaration of x. But you can still refer to the latter as this.x.

Why illegal forward reference error not shown while using static variable with class name

In the below code while accessing a static variable with class name it doesn't throw a forward reference error but accessing it without class name does.
Why this doesn't happen when accessing with class name?
class Test{
static {
System.out.println(a); // shows error
a = 99; // and this line too doesn't give error
System.out.println(Test.a); // this line doesn't
}
static int a = 10;
static{
System.out.println(a);
}
}
The rules for forward reference is defined in JLS §8.3.3:
Use of class variables whose declarations appear textually after the
use is sometimes restricted, even though these class variables are in
scope (§6.3). Specifically, it is a compile-time error if all of the
following are true:
The declaration of a class variable in a class or interface C appears
textually after a use of the class variable;
The use is a simple name in either a class variable initializer of C
or a static initializer of C;
The use is not on the left hand side of an assignment;
C is the innermost class or interface enclosing the use.
So, basically your first Sysout(), satisfies all the above 4 conditions, and hence it's a compile time error.
In the 2nd Sysout(), you're accessing a using it's qualified name, rather than simple name, which as per the above rules is allowed.
Now, the reason for this would be, when you access Test.a, the compiler is sure that Test class has been loaded and all static fields have been initialized, so it can access the field a. But while accessing a on simple name, the compiler isn't sure that initializer for a has already run or not, since it might still be in process of loading the class.
Consider the following process of loading of class:
When a class is loaded, memory is allocated for all the static variables declared in it. At this point, the variable a has got its memory allocated (declaration done)
Then all the static initializers run in the order of occurrence.
First statement is Sysout(a);. a hasn't been initialized yet, so you can't access it. (error)
Second statement is a = 99. Here you're actually initializing the variable a. Perfectly fine.
Third one is Sysout(Test.a) - reasoning for this is already posted above. Compiler knows Test is already loaded.
Then static int a = 10 is executed. It re-initializes a to 10. Remember, declaration part is already taken care of in first step.
First , let's see what JLS have to say for illegal forward references.
Use of class variables whose declarations appear textually after the
use is sometimes restricted, even though these class variables are in
scope (§6.3). Specifically, it is a compile-time error if all of the
following are true:
The declaration of a class variable in a class or interface C appears
textually after a use of the class variable;
The use is a simple name in either a class variable initializer of C
or a static initializer of C;
The use is not on the left hand side of an assignment;
C is the innermost class or interface enclosing the use.
Use of instance variables whose declarations appear textually after
the use is sometimes restricted, even though these instance variables
are in scope. Specifically, it is a compile-time error if all of the
following are true:
The declaration of an instance variable in a class or interface C
appears textually after a use of the instance variable;
The use is a simple name in either an instance variable initializer of
C or an instance initializer of C;
The use is not on the left hand side of an assignment;
C is the innermost class or interface enclosing the use.
It defines what is a illegal forward reference for each - static and instance variables. However, definition seems to be same for both of them. Since your question asks about static, we will dig deep into this only.
1) See, for a static variable, there is declaration and there is initialization.
static int a; //only declaration
static int b = 10; //both declaration and initialization
2) Use of class variables whose declarations appear textually after the use is sometimes restricted, even though these class variables are in scope.
What does it explains to us ? It says that there are cases when we can use static variables even if we declare them afterwards. And in some cases this wont be allowed. So, what are the cases that this won't be allowed ?
If the following 4 conditions are true at the same time. Else you are free to use it even though you have declared it afterwards.
a) The declaration of a class variable in a class or interface C appears textually after a use of the class variable;
b) The use is a simple name in either a class variable initializer of C or a static initializer of C;
c) The use is not on the left hand side of an assignment;
d) C is the innermost class or interface enclosing the use.
Well, the a) point is simple. It says that you must be looking at these rules only when you want to use a static variable before declaring it else why would you dig up into this JLS.
The b) point is that if you use simple name like boy [and not class name appended to it like MyClass.boy only then you might enter the problem of illegal forward reference else you are safe my friend. But just this condition don't qualifies for Illegal forward reference else your a=99 in you code would have given us error immediately. There are 2 more conditions that HAVE to be true for this error to generate . If the below 2 does not qualify, you are allowed to use it like this.]
The point c) is pretty straightforward. Are you not using in left hand side of assignment ? If we look at a=99 , Nope !! System.out.println(a) - This is not even an assignment. Hence no left hand assignment is true.
The point d) is also simple. It just tell you which class/Interface C he means in definition.Your C = Test.
Now let's revisit your code. At each line of code, I will comment True+False+True+false something like this meaning that for each line what point a), b) , c) ,d) will give to each of line. Okay ?
class Test {
static {
System.out.println(a); // True + True + True +True ; Illegal forward reference
a = 99; // True + True + False +True ; No illegal forward reference
System.out.println(Test.a); // True + False + True + True No illegal forward reference
}
static int a = 10;
static {
System.out.println(a);
}
}
The next question one may think is now what it will print ? What values will it take if we use before declaring it ?
Now we come to rules of static initialization. Here, we will go according to your code.
When class is loaded and no illegal forward reference is not there , all the static variables have been initialized to default value and are present in memory.
Now, after this static initialization takes place.
class Test {
static {
System.out.println(Test.a); // prints 0
a = 99; // a get 99
System.out.println(Test.a); // prints 99
}
static int a = 10;
static {
System.out.println(a); // prints 10
}
public static void main(String[] args) {
}
}
Well, the static block
static {
System.out.println(a);
a = 99;
System.out.println(Test.a);
}
will be executed before a is declared.
The line with Test.a won't fire any error because the compiler checks and finds static variable a declared within class Test.
Class initializers are run at the time that the class is loaded, and you cannot determine when exactly this happens. Code that runs within a class initializer should only be for things that need to be processed or "initialized" for other static methods (or the class itself) to work properly.
Additionally, you are referencing a variable that has not yet been declared. In your class initializer (the first static block) you assign a = 99, but a has not yet been declared. If anything you'd want to declare the variable, and then initialize it within the static block.
There is no reason to use class initializers for the type of code you are posting. This should be a static method if anything.
Here's an example
class Test{
static int a = 10;
static void doSomething(){
System.out.println(a);
a = 99;
System.out.println(a);
}
}
Then in main you can call Test.doSomething();

What really happens when loading a class in java?

I'm confused with the process of loading a class. What is the order in which members of a class are executed?
See the following:
class L {
static void fr(){
a=1;
b=3;
a=b;
}
static{
a=3;
b=1;
a=b;// here the problem:cannot reference a field before it is defined
}
static int a;
static int b;
public static void main(String args[]) {
}
}
Whenever I move the declarations of a and b to the top before the static block, compilation works fine. So I need to understand how this stuff works to resolve the problem above.
In Java, it is illegal to refer to a class variable before it is declared, if it's not an expression initializing it. Java will execute initializers in a class in textual order.
Section 12.4.2 of the JLS states:
Next, execute either the class variable initializers and static initializers of the class, or the field initializers of the interface, in textual order, as though they were a single block.
Section 8.3.3 of the JLS states:
Use of class variables whose declarations appear textually after the use is sometimes restricted, even though these class variables are in scope (§6.3). Specifically, it is a compile-time error if all of the following are true:
The declaration of a class variable in a class or interface C appears textually after a use of the class variable;
The use is a simple name in either a class variable initializer of C or a static initializer of C;
The use is not on the left hand side of an assignment;
C is the innermost class or interface enclosing the use.
All of these are true. The declaration appears after the use in a=b;. It's in a static initializer. It's not on the left side of an assignment, and it's the innermost (only) class.
The simplest way to get this to compile is to move the declarations above the static initializer in the source code.
Interestingly, replacing a=b; with a=L.b; will also get this to compile, because the reference to b is no longer "simple".
First the class is loaded and the overall structure verified. Then the methods are verified and external linkages checked. Then the static init is performed. This is when the static block is executed.
But your problem is apparently with the compiler, not class loading. You need to move your static variable declarations up above the static block. Has nothing at all to do with class loading.
Hint: Learn the difference between a compile-time error and a run-time exception. What you were seeing was an error message from the compiler. (And you're lucky you didn't catch bloody hell and close votes for not including the EXACT error message in your question.)

Why can you access static field before it is defined through a method in Java?

I ran into an interesting thing:
static {
System.out.println(test); // error cannot reference a field before it is defined
System.out.println(cheat()); // OK!
}
private static boolean cheat() {
return test;
}
private static boolean test = true;
public static void main(String args[]) {}
The first way is wrong and both your compiler and IDE will tell you it's wrong. In the second case, cheating is OK, but it actually defaults the field test to false. Using Sun JDK 6.
This is defined in the JLS 8.3.2.3. In particular:
The declaration of a member needs to appear textually before it is used [...] if the usage occurs in a [...] static initializer of C.
When you call cheat() you go around that rule. This is actually the 5th example in the list of the examples of that section.
Note that cheat() will return false in the static initializer block because test has not been initialised yet.
Because class loading works in this order:
Loads Class definition (methods, signatures)
Allocates the memory for the static variable references (for test) - does not initialize yet
Executes the static initializers (for variables) and the static blocks - in order they are defined
So, by the time you have reachstatic block, you have the method definition ready, but don't have the variable ready. With cheat() you're actually reading an uninitialized value.
This is the generic steps by which class loading happens.
Loading - Load the class to memory
Verification - checks binary representation of a class e is correct
Preparation - create the static fields for the class and initialize those fields to their standard default values.
Initializing - will invoke static initializers and initializers for static fields
After preparation, your test will be false.Then before you assign static variable to true, your static block will execute.That is why you are getting false.
Try making your static variable final.In that case,you will be getting true.This is because your compiler itself will embed the value in bytecode(since the field is final) as part of optimisation

Categories

Resources