why static block cannot access the static variable defined after it - java

I've checked Forward References During Field Initialization and this the answer from #assylias, but still I got no answer to the why.
Why a static block can assign the static variable declared after it but can NOT access it?
class Parent {
static {
i = 2; // valid
// can only assign new value to it instead of accessing it?
// System.out.println(i); // invalid - compile-error
}
static int i = 0;
static {
i = 3; // valid
}
}
Is it due to the fact: the value is not initialized yet, so we just explicitly inhibit you from using it? or there are something related to security I don't know?
updated
this is not a duplicate of that problem which is about
Why this doesn't happen when accessing with class name?
This question is about why we have this design? for what purpose?

Static fields are initialized based on the order they appear in the code.
So, when you assign a value to i variable you just say to the compiler: "Hey, guy, when you get to initialize this variable, set its value to...". But you can not use it until it's initialized because it simply does not exist yet.
UPDATE:
As it says in the book "The Java Language Specification" by James Gosling, Bill Joy, Guy Steele and Gilad Bracha:
These restrictions are designed to catch, at compile time, circular or
otherwise malformed initializations.
Consider this:
static {
i = 2;
j = i + 5; //should it be 7 or 15?
}
static int i = 10;
static int j;
Should j variable be 7 or 15?
If it's 7, then we have initialized i variable twice, which is not possible, since the field is static. If it's 15 then what does i = 2; mean?
This code is ambiguous, so Java Specification does not allow to do that.

After some further reading, I think Pavel is not quite accurate in this point as #Holger pointed out in the comment.
we have initialized i variable twice, which is not possible, since the field is static.
As the 12.4.2. Detailed Initialization Procedure points out
For each class or interface C, there is a unique initialization lock LC. The mapping from C to LC is left to the discretion of the Java Virtual Machine implementation.
I suppose the initialization twice is okay to the class initializer itself as long as it's just once to the invoking clients.
But the demos Pavel provided still stands its position so I basically just reuse it here but with different explanation.
static {
i = 2;
j = i + 5;
// no one knows whether "i" here initialized properly here
}
static int i = 10;
static int j;
But when you use MyClass.i directly in j = MyClass.i + 5, the compiler would know it would then be okay as 8.3.3. Forward References During Field Initialization detailed with four conditions.
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.
And there is a detailed discussion in this answer already.
To wind it up, I think this would be for predictable behavior to add these restrictions. Once again, the other official purpose pointed out in 8.3.3. Forward References During Field Initialization.
These restrictions are designed to catch, at compile time, circular or otherwise malformed initializations.

The same is also applicable to the non-static members. You can assign values to them in instance initializer block but can not use them before initialization.
class Parent {
{
x = 3; // works fine
// System.out.println(x); // gives compilation error.
}
int x = 0;
public static void main(String[] args) {
}
}

Related

final variable initialized first

I was reading the question here : Java : in what order are static final fields initialized?
According to the answer
"except that final class variables and fields of interfaces whose
values are compile-time constants are initialized first ..."
I think this is not correct because the following will fail :
static {
String y = x;
}
public static final String x = "test";
In the static block, x is not recognized. Can anyone please comment if that answer is correct ?
The order of initialization doesn't change the fact that the JLS doesn't let you refer to variables before they're declared in various cases. This is described 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.
That's why your code gets this compilation erorr:
error: illegal forward reference
The statement that static fields that are constant variables are initialized first is indeed defined in JLS§12.4.2:
Otherwise, record the fact that initialization of the Class object for C is in progress by the current thread, and release LC.
Then, initialize the static fields of C which are constant variables (§4.12.4, §8.3.2, §9.3.1).
...
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.
As you can see, constant variables are initialized in step 6, but others in step 9.
This demonstrates the behavior:
public class Example {
static String y;
static {
y = foo();
}
static String foo() {
return x.toUpperCase();
}
public static final String x = "test";
public static void main(String[] args) throws Exception {
System.out.println(x);
System.out.println(y);
}
}
That compiles, and outputs:
test
TEST
In contast, if you change the x line so it's not constant anymore:
public static final String x = Math.random() < 0.5 ? "test" : "ing";
It compiles, but then fails because x is null as of y = foo();.
For the avoidance of doubt: I don't recommend using methods to initialize fields like that. :-)
As this answer states:
... they are initialized in the order in which they appear in the source.
You are absolutely right, your example fails, because you are trying to use a variable that is declared after the usage. Since static block are executed in order of the source code, you are absolutely correct and should suggest and edit for that answer, because this statement is invalid:
except that final class variables ... are initialized first.

What variable is being referred to here?

class Test {
int a;
void method() {
a = 1;
int a = a = 2;
a = 3;
}
}
There are lots of as in method. What are they all referring to?
This is a simple example of the bizarreness of Java's scoping rules.
a = 1;
int a = a = 2;
a = 3;
Breaking it down line-by-line:
a = 1; is referring to the member variable.
a = 3; is referring to the local variable, because it's after the declaration of the local variable. It's pretty confusing that you can refer to two different symbols via the same identifier, in the same method.
int a = a = 2;: the second a is the local variable.
The self-reference in the variable declaration is really curious. You can find this in the language spec:
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.
It is also true that member variables can refer to themselves in their own initializer; but this is for a slightly different reason:
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.
I have yet to find a compelling reason for the existence of this rule for local variables; maybe you just really really want to make sure it's assigned. The only thing that I can think it allows you to do is to use the variable as temporary storage in the evaluation of the initializer, for example:
int a = method(a = somethingThatIsReallyExpensiveToCompute(), a);
would be equivalent to:
int a;
{
int tmp = somethingThatIsReallyExpensiveToCompute();
a = method(tmp, tmp);
}
Personally, I'd rather see the second form of the code, as the evaluation of the first just seems obscure.

Accessing local variables from method local inner class in which we declare the inner class

I am learning Java, the tutorial i am following says Java does not allow the variable k (local variable to m1() in the below example) to be directly accessible from method m2() because they are created in the same method m1() and that I will get an error during compile time (unless k is declared as final). The reason they say is a local variable (k) is created during method is called and destroyed after the method execution is complete, but an object (o) is created when the object is instantiated and can still be not destroyed after method execution. So the tutorial says that if you call the method m2() or object o after method m2 is executed (I do not know how it is possible) variable k will be destroyed and will not be available. So the tutorial claims that Java will not allow such declarations. (Please feel free to correct me wherever my understanding is wrong)
But when I compile this program works fine. Am I missing something? I understand this is a bit complex from my perspective of explaining so if my question is clear so please feel free to ask me if something is not clear.
Thanks in advance for your help.
class innerclass_ex8
{
int x = 10;
static int y = 20;
public void m1()
{
int k = 30;
final int z = 50;
class inner {
public void m2()
{
System.out.println(x);
System.out.println(y);
System.out.println(k);
System.out.println(z);
}
}
inner o = new inner();
o.m2();
}
public static void main(String[] args)
{
innerclass_ex8 g = new innerclass_ex8();
g.m1();
}
}
First of all, your program compiles and works fine because you are using Java 8. If using Java 7 or lower, it won't even compile.
The reason is exactly as you cited. But I will try to explain it a bit more. Consider the following code:
public void m1() {
int k = 30;
class inner {
public void m2() {
System.out.println(k);
}
}
inner o = new inner();
k = 42; // <= Note the reassignment here.
o.m2();
}
What should the method call o.m2() print? "30" or "42"? Both outputs could reasonably be argumented. At the time the method was declared and defined, the variable k had the value 30. At the time the method was called, the variable k had the value 42.
To prevent such ambiguities, the compiler does not allow assignment to a variable that is used in such inner classes (local and anonymous). So it must be final.
In Java 8 this was relaxed a bit. Java 8 introduced the concept of effectively final. A variable that is declared and initialized and not being assigned again is considered effectively final. And the compiler allows that code without declaring the variable final.
As a matter of fact, you also get a compiler error in Java 8 when trying to compile the above code.
Concept you told about variable inside method is correct. If you compile if with Java 1.7 or less then it will throw compile error. I guess you are using java 1.8. There is a concept call final variable and effectively final variable. In java 1.8 if method variable value is assigned and not changed again then it can execute this code without throwing compile time
error.

Will Java Final variables have default values?

I have a program like this:
class Test {
final int x;
{
printX();
}
Test() {
System.out.println("const called");
}
void printX() {
System.out.println("Here x is " + x);
}
public static void main(String[] args) {
Test t = new Test();
}
}
If I try to execute it, i am getting compiler error as : variable x might not have been initialized based on java default values i should get the below output right??
"Here x is 0".
Will final variables have dafault values?
if I change my code like this,
class Test {
final int x;
{
printX();
x = 7;
printX();
}
Test() {
System.out.println("const called");
}
void printX() {
System.out.println("Here x is " + x);
}
public static void main(String[] args) {
Test t = new Test();
}
}
I am getting output as:
Here x is 0
Here x is 7
const called
Can anyone please explain this behavior..
http://docs.oracle.com/javase/tutorial/java/javaOO/initial.html, chapter "Initializing Instance Members":
The Java compiler copies initializer blocks into every constructor.
That is to say:
{
printX();
}
Test() {
System.out.println("const called");
}
behaves exactly like:
Test() {
printX();
System.out.println("const called");
}
As you can thus see, once an instance has been created, the final field has not been definitely assigned, while (from http://docs.oracle.com/javase/specs/jls/se7/html/jls-8.html#jls-8.3.1.2):
A blank final instance variable must be definitely assigned at
the end of every constructor of the class in which it is
declared; otherwise a compile-time error occurs.
While it does not seem to be stated explitely in the docs (at least I have not been able to find it), a final field must temporary take its default value before the end of the constructor, so that it has a predictable value if you read it before its assignment.
Default values: http://docs.oracle.com/javase/specs/jls/se7/html/jls-4.html#jls-4.12.5
On your second snippet, x is initialized on instance creation, so the compiler does not complain:
Test() {
printX();
x = 7;
printX();
System.out.println("const called");
}
Also note that the following approach doesn't work. Using default value of final variable is only allowed through a method.
Test() {
System.out.println("Here x is " + x); // Compile time error : variable 'x' might not be initialized
x = 7;
System.out.println("Here x is " + x);
System.out.println("const called");
}
JLS is saying that you must assign the default value to blank final instance variable in constructor (or in initialization block which is pretty the same). That is why you get the error in the first case. However it doesn't say that you can not access it in constructor before. Looks weird a little bit, but you can access it before assignment and see default value for int - 0.
UPD. As mentioned by #I4mpi, JLS defines the rule that each value should be definitely assigned before any access:
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.
However, it also has an interesting rule in regards to constructors and fields:
If C has at least one instance initializer or instance variable initializer then V is [un]assigned after an explicit or implicit superclass constructor invocation if V is [un]assigned after the rightmost instance initializer or instance variable initializer of C.
So in second case the value x is definitely assigned at the beginning of the constructor, because it contains the assignment at the end of it.
If you don't initialize x you'll get a compile-time error since x is never initialized.
Declaring x as final means that it can be initialized only in the constructor or in initializer-block (since this block will be copied by the compiler into every constructor).
The reason that you get 0 printed out before the variable is initialized is due to the behavior defined in the manual (see: "Default Values" section):
Default Values
It's not always necessary to assign a value when a field is declared.
Fields that are declared but not initialized will be set to a
reasonable default by the compiler. Generally speaking, this default
will be zero or null, depending on the data type. Relying on such
default values, however, is generally considered bad programming
style.
The following chart summarizes the default values for the above data
types.
Data Type Default Value (for fields)
--------------------------------------
byte 0
short 0
int 0
long 0L
float 0.0f
double 0.0d
char '\u0000'
String (or any object) null
boolean false
The first error is the compiler complaining that you have a final field, but no code to initialize it - simple enough.
In the second example, you have code to assign it a value, but the sequence of execution means you reference the field both before and after assigning it.
The pre-assigned value of any field is the default value.
All non-final fields of a class initialize to a default value (0 for nummeric data types, false for boolean and null for reference types, sometimes called complex objects). These fields initialize before a constructor (or instance initialization block) executes independent of whether or not the fields was declared before or after the constructor.
Final fields of a class has no default value and must be explicitly initialized just once before a class constructor has finished his job.
Local variables on the inside of an execution block (for example, a method) has no default value. These fields must be explicitly initialized before their first use and it doesn't matter whether or not the local variable is marked as final.
Let me put it in the simplest words I can.
final variables need to be initialized, this is mandated by the Language Specification.
Having said that, please note that it is not necessary to initialize it at the time of declaration.
It is required to initialize that before the object is initialized.
We can use initializer blocks to initialize the final variables. Now, initializer blocks are of two types
static and non-static
The block you used is a non-static initializer block. So, when you create an object, Runtime will invoke constructor and which in turn will invoke the constructor of the parent class.
After that, it will invoke all the initializers (in your case the non-static initializer).
In your question, case 1: Even after the completion of initializer block the final variable remains un-initialized, which is an error compiler will detect.
In case 2: The initializer will initialize the final variable, hence the compiler knows that before the object is initialized, the final is already initialized. Hence, it will not complain.
Now the question is, why does x takes a zero. The reason here is that compiler already knows that there is no error and so upon invocation of init method all the finals will be initialized to defaults, and a flag set that they can change upon actual assignment statement similar to x=7.
See the init invocation below:
As far as I'm aware, the compiler will always initialize class variables to default values (even final variables). For example, if you were to initialize an int to itself, the int would be set to its default of 0. See below:
class Test {
final int x;
{
printX();
x = this.x;
printX();
}
Test() {
System.out.println("const called");
}
void printX() {
System.out.println("Here x is " + x);
}
public static void main(String[] args) {
Test t = new Test();
}
}
The above would print the following:
Here x is 0
Here x is 0
const called
If I try to execute it, i am getting compiler error as : variable x might not have been initialized based on java default values i should get the below output right??
"Here x is 0".
No. You are not seeing that output because you are getting a compile-time error in the first place. Final variables do get a default value, but the Java Language Specification (JLS) requires you to initialize them by the end of the constructor (LE: I'm including initialization blocks here), otherwise you'll get a compile-time error which will prevent your code to be compiled and executed.
Your second example respects the requirement, that's why (1) your code compiles and (2) you get the expected behaviour.
In the future try to familiarize yourself with the JLS. There's no better source of information about the Java language.

The use of "this" in Java

If I write the following class:
public class Example {
int j;
int k;
public Example(int j, int k) {
j = j;
k = k;
}
public static void main(String[] args) {
Example exm = new Example(1,2);
System.out.println(exm.j);
System.out.println(exm.k);
}
}
The program compiles, but when I run the program, the main method will print out two 0s. I know that in order to say that I want to initialize the instance variables in the constructor I have to write:
this.j = j;
this.k = k;
But if I don't write it, then which variable is evaluated (or considered) in the constructor (on the left and on the write hand side of the expressions)? Is is the argument or the instance variable? Does it make a difference?
Are there other cases where the use of this is obligatory?
If you don't write "this.variable" in your constructor, and if you have a local variable (including the function parameter) with the same name as your field variable in the constructor, then the local variable will be considered; the local variable shadows the field (aka class variable).
One place where "this" is the only way to go:
class OuterClass {
int field;
class InnerClass {
int field;
void modifyOuterClassField()
{
this.field = 10; // Modifies the field variable of "InnerClass"
OuterClass.this.field = 20; // Modifies the field variable of "OuterClass",
// and this weird syntax is the only way.
}
}
}
If you say just j in your constructor then the compiler will think you mean the argument in both cases. So
j = j;
simply assigns the value of the argument j to the argument j (which is a pretty pointless, but nonetheless valid statement).
So to disambiguate this you can prefix this. to make clear that you mean the member variable with the same name.
The other use of this is when you need to pass a reference to the current object to some method, such as this:
someObject.addEventListener(this);
In this example you need to refer to the current object as a whole (instead of just a member of the object).
If you don't write this, then you assign the argument to itself; the argument variables shadow the instance variables.
this is useful when you want to return the object itself
return this;
This is useful because if a class has for example Method1() and Method2(), both returning this, you are allowed to write calls like
object.Method1().Method2()
Also inside a method it can be useful to pass the object itself to another function, during a call.
Another useful approach (though seldom used) is to declare method parameters final:
The following block will not compile, thus alerting you to the error immediately:
public Example(final int j, final int k) {
j = j;
k = k;
}
In your constructor code, you are assigning variables to themselves. 'j' is the j specified in the argument for the constructor. Even if it was the class variable j defined above, then you are still saying "j = j" ... i.e. j isn't going to evaluate differently on the left and on the right.
public Example(int j, int k) {
this.j = j;
this.k = k;
}
What you are experiencing is called variable shadowing. Have a look at this overview for different kinds of variables in Java.
Generally speaking: The Java compiler uses the nearest variable it can find for an assignment. In a method it will first try to find a local variable and then enlarge the focus of its search to class and instance variables.
One habit I personally find good (others don't like it) is prefixing a member variable with m_ and using uppercase for CONSTANT_VARIABLES that don't change their value. Code where variable shadowing is used on purpose is very(!) difficult to debug and work with.
You assign the parameter to itself in your example.
More generally speaking: If you don't prepend a scope to your variable, the current scope is assumed - which is the function in your case. 'this.j' tells the jre to use the variable j within the object scope - the member variable of the object.
Just like Robert Grant said, 'this' is how you make it clear that you are referring to a member variable instead of a local variable.
To answer your follow-up question about this with method, the inner class mentioned by Srikanth is still a valid example of using this: (with method this time)
public class OuterClass
{
void f() {System.out.println("Outer f()");};
class InnerClass {
void f() {
System.out.println("Inner f()");
OuterClass.this.f();
}
}
}
You have the same situation with anonymous class:
You can refer to the outer class’s methods by:
MyOuterClass.this.yOuterInstanceMethod(),
MyOuterClass.myOuterInstanceMethod(),
or simply myOuterInstanceMethod() if there is no ambiguity.
This doesn't quite answer your question, but if you use Eclipse you might find "Assignment has no effect" setting useful. I am sure this will be in other IDEs too.
You might want to take a look at What is the advantage of having this/self pointer mandatory explicit?
Although using this is not mandatory in Java as you noticed I'm sure it will shed some light on the subject of using this in Java as well.
To avoid this, use an IDE (like Eclipse) and it will generate warnings in this case.
Also, make your fields final unless they absolutely can't be. There are a number of reasons (apart from this one) to do that.

Categories

Resources