Access to final field via getter before its initializing - java

First of all, this question has forward relation to these nice questions:
1) Use of uninitialized final field - with/without 'this.' qualifier
2) Why isn't a qualified static final variable allowed in a static initialization block?
But I will ask it in slightly another angle of view. Just to remember: mentioned questions above were asked about access to final fields using keyword this in Java 7.
In my question there is something similar, but not the same. Well, consider the following code:
public class TestInit {
final int a;
{
// System.out.println(a); -- compile error
System.out.println(getA());
// a = a; -- compile error
a = getA();
System.out.println(getA());
}
private int getA() {
return a;
}
public static void main(String[] args) {
new TestInit();
}
}
And output is:
0
0
As you can see there are two unclear things here:
There is another legal way to access non-initialized final field: using its getter.
Should we consider that an assigment a = getA(); as legal for blank final field and that it will always assign to it its default value like for non-final field according to JLS? In other words, should it be considered as expected behaviour?

What you're really running into is the inference ability of the compiler.
For your first compiler error, that fails because the compiler definitely knows a is not assigned. Same with the second a=a (you can't assign from a because it's definitely not assigned at that point).
The line that works a=getA() is a first, single, definite assignment of a, so that's fine (regardless where the value comes from; the implementation of getA() doesn't matter and isn't assessed at this point).
The method getA() is valid and can return a because the instance initializer has definitely assigned a value to a.
It makes sense to a human looking at it, but it's not an inference built into the compiler. Each block, independently evaluated, is valid.

Related

Why does javac allow fields to be defined after methods that use them in a class?

I have noticed that when you execute a statement like sum=a+b;
we need the three fields initialized prior to using them in this operation
ie
int a=1
int b=1;
int sum=0;
sum=a+b;
.
My understanding is, this is because javac works like an interpreter and reads line by line.
But when it comes to methods where we call and pass values to fields inside a class we can define the fields after method declaration.
public class Dog extends Animal{
public Dog(String name, int weight, int teeth, String coat) {
super(name, true, true, weight);
this.tail=true;
this.eyes=2;
this.teeth=teeth;
this.coat=coat;
}
private int eyes;
private boolean tail;
private int teeth;
private String coat;}
The parameters eyes, tail, teeth, and coat have been defined after they were used to pass values to the constructor.
How does javac understand that there is a variable after the method.
Is this allowed because we can only define fields and not do any operations on them in a class definition?
Javac is Java Compiler Compiles your Java code into Bytecode
JVM is Java Virtual Machine Runs/ Interprets/ translates Bytecode into Native Machine Code
This error occurs only with local variables because the compiler does not set the default value for the local variables. In the case of instance variables, the compiler sets default values, for example, 0 for integer, null for string, etc.
In the above case if you don't set values in the constructor still your instance variables will be initialized by default values.
private int eyes = 0;
private boolean tail = false;
private int teeth = 0;
private String coat = null;
But same is not true for local variables a, b or sum.
sum can be uninitialized because you are assigning the values to the sum before using it. That is the statement sum = a+b will assign value and before you use it somewhere else may be like sum2 = sum, its assured that sum has a value.
A local variable declaration (and eventual initialization) is executed together with the block it is in, and being accessed from;
an instance field declaration (and initialization) is executed when the instance is created, before the methods are executed 1.
On the other side this is not allowed:
public class Dog extends Animal {
private int total = eyes + teeth; // ERROR!
private int eyes = 2;
private int teeth = 24;
}
above code does not compile - that is intentional!
while this is valid:
public class Dog extends Animal {
private int eyes = 2;
private int teeth = 24;
private int total = eyes + teeth; // OK
}
Why ...? Short answer: it is specified that way, see Java Language Specification (JLS) - Scope of a Declaration:
The scope of a declaration of a member m declared in ... a class or interface C (§8.2, §9.2) is the entire body of C, ...
The scope of a local variable declared in a block by a local variable declaration statement (§14.4.2) is the rest of the block,...
One reasons for that is to avoid cyclic references as mentioned in the JLS, see examples on link below 1. Also explained in this answer by Hoopje.
1 - exception are references to fields in initializers, see Restrictions on Field References in Initializers
This has nothing to do with the difference between an interpreter and a compiler. It is just a rule added to language to prevent two fields to refer to each other in their initialization code. For example, what would be the initial values of the fields in the following class?
class A {
int a = b * b;
int b = a + 1;
}
Of course, the Java designers could have decided to allow forward references, and only generate an error when such cycles are detected. But they decided otherwise, and I think that that was the right decision.
There is no reason to forbid forward references to fields from within a method, because when the method is executed, the class is initialized already. Even constructors run after the fields' initializers have been evaluated.

Why must I use the "this" keyword for forward references?

When I use the this keyword for accessing a non-static variable in a class, Java doesn't give any error. But when I don't use it, Java gives an error. Why must I use this?
I know when should normally I use this, but this example is very different from normal usages.
Example:
class Foo {
// int a = b; // gives error. why ?
int a = this.b; // no error. why ?
int b;
int c = b;
int var1 = this.var2; // very interesting
int var2 = this.var1; // very interesting
}
The full description is in section 8.3.3 of the Java Language Specification: "Forward References During Field Initialization"
A forward reference (referring to a variable that is not declared yet at that point) is only an error if the following are all 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.
See the bolded text: "the use is a simple name". A simple name is a variable name without further qualification. In your code, b is a simple name, but this.b is not.
But why?
The reason is, as the cursive text in the example in the JLS states:
"The restrictions above are designed to catch, at compile time,
circular or otherwise malformed initializations. "
In other words, they allow this.b because they think that a qualified reference makes it more likely that you have thought carefully about what you're doing, but simply using b probably means that you made a mistake.
That's the rationale of the designers of the Java Language. Whether that is true in practice has, to my knowledge, never been researched.
Initialization order
To expand on the above, in reference to Dukeling's comment on the question, using a qualified reference this.b will likely not give you the results you want.
I'm restricting this discussion to instance variables because the OP only referred to them.
The order in which the instance variables are assigned is described in JLS 12.5 Creation of New Class Instances.
You need to take into account that superclass constructors are invoked first, and that initializations code (assignments and initialization blocks) are executed in textual order.
So given
int a = this.b;
int b = 2;
you will end up with a being zero (the value of b at the time that a's initializer was executed) and b being 2.
Even weirder results can be achieved if the superclass constructor invokes a method that is overridden in the subclass and that method assigns a value to b.
So, in general, it is a good idea to believe the compiler and either reorder your fields or to fix the underlying problem in case of circular initializations.
If you need to use this.b to get around the compiler error, then you're probably writing code that will be very hard to maintain by the person after you.
Variables are declared first and then assigned. That class is the same as this:
class Foo {
int a;
int b;
int c = b;
int var1;
int var2;
public Foo() {
a = b;
var1 = var2;
var2 = var1;
}
}
The reason you can't do int a = b; is because b is not yet defined at the time the object is created, but the object itself (i.e. this) exists with all of its member variables.
Here's a description for each:
int a = b; // Error: b has not been defined yet
int a = this.b; // No error: 'this' has been defined ('this' is always defined in a class)
int b;
int c = b; // No error: b has been defined on the line before
You have presented 3 cases:
int a = b;
int b;
This gives error because the compiler will look for b in the memory and it will not be there. but when you use this keyword then it specifies explicitly that the b is defined in the scope of the class, all class references will be looked up for it, and finally it will find it.
Second scenario is pretty simple and as I described, b is defined in the scope before c and will not be a problem while looking for b in the memory.
int var1 = this.var2;
int var2 = this.var1;
In this case no error because in each case the variable is defined in the class and assignment uses this which will look for the assigned variable in the class, not just the context it is followed by.
For any class in Java this is a default reference variable (when no specific reference is given) that either user can give or the compiler will provide inside a non-static block. For example
public class ThisKeywordForwardReference {
public ThisKeywordForwardReference() {
super();
System.out.println(b);
}
int a;
int b;
public ThisKeywordForwardReference(int a, int b) {
super();
this.a = a;
this.b = b;
}
}
You said that int a = b; // gives error. why ? gives compile time error because b is declared after a which is an Illegal Forward Reference in Java and considered as a compile-time error.
But in the case of methods Forward Reference becomes legal
int a = test();
int b;
int test() {
return 0;
}
But in my code, the constructor with the argument is declared before both a & b, but not giving any compile-time error, because System.out.println(b); will be replaced by System.out.println(this.b); by the compiler.
The keyword this simply means current class reference or the reference on which the method, constructor or the attribute is accessed.
A a1 = new A(); // Here this is nothing but a1
a1.test(); // Here this is again a1
When we say a = this.b; it is specifying that b is a current class attribute, but when we say a = b; since it is not inside a non-static block this won't be present and will look for an attribute declared previously which is not present.
Please look at the Java Language Specification: https://docs.oracle.com/javase/specs/jls/se7/html/jls-8.html#jls-8.3.2.3
This is the reason, IMO: The usage is via a simple name.
So in this case you have to specify the name using this.

scopes of variables in Java

In Java, you don't have declare a method physically before you use it. The same thing doesn't apply for variables.
Why is this the case? Is it just for "legacy" reason (ie., the Java's creators didn't feel like doing it), or is it just not possible?
Eg.,
public class Test
{
// It is OK for meth1 to invoke meth2
public void meth1() { meth2(); }
public void meth2() { }
// But why is it NOT ok for field1 to reference field2
private int field1 = field2;
private int field2 = 3;
}
If I wanted my Java compiler to support this kind of forward-reference, what is the general idea as to how to do it?
I understand there'd be issue about circular depencies, which we'd need to be careful about. But other than that, I really don't see why it should not be possible.
[Edit]Ok, here's my initial thought as to how to do this.
While analysing the code, the compiler would build a graph of dependencies for the variables in the given scope. And if it sees a loop (ie., int a = b; int b = a), then it would throw an error. If there is no loops, then there must be some optimal way to re-arrange the statements (behind the scence) such that a field will only reference fields declared before it, and so it shouuld try to figure out the order. I haven't worked out the exact algorithm, but I think it is possible. Unless someone can scientifically prove me wrong.
Recap the question:
Say I'm trying to build my own dialect of Java, which supports this sort of scoping. My main question is, could you give me some ideas as to how to do this
Thanks
According to the JLS, Section 12.4.1, initialization of class variables proceeds from top to bottom, in "textual order":
The static initializers and class variable initializers are executed in textual order, and may not refer to class variables declared in the class whose declarations appear textually after the use, even though these class variables are in scope (§8.3.2.3). This restriction is designed to detect, at compile time, most circular or otherwise malformed initializations.
So, if you make your own compiler to recognize forward class variable declarations, then it is violating the Java Language Specification.
I will give you a simple piece of code:
public class Test
{
private int foo = bar;
private int bar = foo;
}
What would you expect this to do?
I presume that designers of Java has done it so because assignment of values to instance variables must be executed in some order. In the case of Java, they are executed downwards (from up to bottom).
EDIT
What about this?
public class Test {
private int foo = quu++;
private int bar = quu++;
private int quu = 1;
}
What values would foo and bar have? Which quu++ statement would be executed first?
My point is, Java designers must have thought that it is counter intuitive to do it as you did describe in your question, i.e. unordered execution with compile time code analysis.
FINAL EDIT
Let's complicate things:
class Test {
private James james = new James(anInt);
private Jesse jesse = new Jesse(anInt);
private IntWrapper anInt = new IntWrapper();
}
class James {
public James(IntWrapper anInt) {
if(--anInt.value != 0) {
new Jesse(anInt);
}
else {
anInt.isJames = true;
}
}
}
class Jesse {
public Jesse(IntWrapper anInt) {
if(--anInt.value != 0) {
new James(anInt);
}
else {
anInt.isJames = false;
}
}
}
class IntWrapper {
public int value = 99;
public boolean isJames;
}
I am not sure what it proves regarding your question because I am not sure about your point.
There is no circular dependency here but value of an instance variable of IntWrapper, isJames, depends on the execution order and it might be difficult to detect this kind of stuff with a lexical/semantic analyzer.
How will field1 know value of field2 before it has been defined and assigned a value?
It has to do with the order of initialization. Fields are initialized top to bottom. In your example, when field1 tries to reference field2 in the initializer, the latter is yet to be initialized itself.
The rule about forward references is aimed at catching the most obvious cases of this problem. It does not catch all; for example, you can still do:
private int field1 = getField2();
private int field2 = 3;
private int getField2() { return field2; }
and get field1 intialized to zero where you might be expecting 3.

compiler optimizes away a final field added to a serialized class

This is a strange one, am wondering if this is by design or a compiler bug. Am using Sun Java 6 on a PC, but also seen with Sun Java 5 on a linux box.
Suppose I have a file on disk containing a serialized class. I then add a final field to the class and declare and initialize the field in one statement:
public final int newField = 2;
If I read in an object that was serialized before this field was added, this new field is given a default value of 0, which is correct. However, the compiler replaces occurrences of the field by the constant "2". OTOH, if I instead initialize the new field inside of a constructor:
Data (int d) {
this.oldField = d;
this.newField = 2;
}
then the field does not get optimized away and everything works as expected.
More explicitly, here is my Main class:
import java.io.*;
public class Main {
public static void main(String[] args) {
try {
FileInputStream fis = new FileInputStream ("Data.txt");
ObjectInputStream ois = new ObjectInputStream (fis);
Data d = (Data)ois.readObject();
ois.close();
fis.close();
d.print();
}
catch (Exception e) {
e.printStackTrace();
}
}
}
and here is the Data class:
import java.io.*;
public class Data implements Serializable {
private static final long serialVersionUID = 1;
private int oldField;
private final int newField = 2;
Data(int d) {
this.oldField= d;
}
public void print() { {
System.out.println(this.oldField + " " + this.newField);
}
}
So my Data.txt file has a serialized instance of the original class with just "oldField = 1". So the expected output of running this is
1 0
but instead I get
1 2
whereas if I move the initialization into the constructor the output is correct.
Is this expected behaviour? My feeling is that the compiler should not be optimizing away fields in serialized classes, precisely for this reason. Perhaps that is asking too much. Thanks!
This is by design. If a variable is declared as final, has a primitive type and an initializer which is a constant expressions, then it is a "constant variable". This alters the semantics of initialization among other things. Specifically, the expression value is evaluated at compile time, and then embedded into the code.
Refer to JLS 4.12.4 for details.
When you put the initialization into the constructor, the variable is no longer a "constant variable" and normal initialization occurs.
By the way, I would have thought that a final variable having a different value than what the initializer said was a bad thing, not a good thing. To me, that would be highly unintuitive. At any rate, relying on this kind of behaviour does not strike me as good practice.
It's not clear why you think this is a problem. The compiler is doing exactly what it should do. The class definition says that the final field's value is 2, and that is exactly what you're getting.
The underlying reason is that when the final variable's value is declared in the initializer, the compiler knows its value immediately, so it is able to substitute the actual value as a literal wherever you use the variable. When you initialized it in constructor code, it couldn't see the value so easily so it doesn't do that optimization. It is still free to do so if it can.
In the language of the JLS #4.12.4, this is a 'constant variable', and JLS #13.1 says 'References to fields that are constant variables (§4.12.4) are resolved at compile time to the constant value that is denoted', which is exactly what is happening here.
My feeling is that the compiler should not be optimizing away fields
in serialized classes
Why should the compiler have different rules for serialized classes?
The usage of final variable always provokes compiler optimization. Its doing what it supposed to do.

Initialization of static final fields in Java

public class Main {
static final int alex=getc();
static final int alex1=Integer.parseInt("10");
static final int alex2=getc();
public static int getc(){
return alex1;
}
public static void main(String[] args) {
final Main m = new Main();
System.out.println(alex+" "+alex1 +" "+alex2);
}
}
Can someone tell me why this prints: 0 10 10? I understand that it's a static final variable and its value shouldn't change but it`s a little difficult to understand how the compiler initializes the fields.
It's an ordering problem. Static fields are initialized in the order that they are encountered, so when you call getc() to inititalize the alex variable, alex1 hasn't been set yet. You need to put initialization of alex1 first, then you'll get the expected result.
This situation is covered by JLS 8.3.2.3 "Restrictions on the use of Fields during Initialization".
The JLS rules allows the usage in your Question, and state that the first call to getc() will return default (uninitialized) value of alex.
However, the rules disallow some uses of uninitialized variables; e.g.
int i = j + 1;
int j = i + 1;
is disallowed.
Re some of the other answers. This is not a case where the Java compiler "can't figure it out". The compiler is strictly implementing what the Java Language Specification specifies. (Or to put it another way, a compiler could be written to detect the circularity in your example and call it a compilation error. However, if it did this, it would be rejecting valid Java programs, and therefore wouldn't be a conformant Java compiler.)
In a comment you state this:
... final fields always must be initialized at compile or at runtime before the object creation.
This is not correct.
There are actually two kinds of final fields:
A so-called "constant variable" is indeed evaluated at compile time. (A constant variable is a variable "of primitive type or type String, that is final and initialized with a compile-time constant expression" - see JLS 4.12.4.). Such a field will always have been initialized by the time you access it ... modulo certain complications that are not relevant here.
Other final fields are initialized in the order specified by the JLS, and it is possible to see the field's value before it has been initialized. The restriction on final variables is that they must be initialized once and only once during class initialization (for a static) or during object initialization.
Finally, this stuff is very much "corner case" behavior. A typical well-written class won't need to
access a final field before it has been initialized.
Static final fields whose values are not compile-time constant expressions are initialized in order of declaration. Thus when alex in being initialized, alex1 is not initialized yet, so that getc() returns default values of alex1 (0).
Note that result will be different (10 10 10) in the following case:
static final int alex1 = 10;
In this case alex1 is initialized by a compile-time constant expression, therefore it's initialized from the very beginning.
There is nothing special about static fields, it just that the compiler cannot workout that you are using a method which can access a field before its initialised.
e.g.
public class Main {
private final int a;
public Main() {
System.out.println("Before a=10, a="+getA());
this.a = 10;
System.out.println("After a=10, a="+getA());
}
public int getA() {
return a;
}
public static void main(String... args) {
new Main();
}
}
prints
Before a=10, a=0
After a=10, a=10
Class variables are not necessary to initialize, they are automatically set to their default values. If primitives (like int, short...) it's 0 (zero) for Objects it's null.
Therefore alex1 is set to 0.
Method variables must be initialized, otherwise you will get an compiliation error.
For a better explanation read http://download.oracle.com/javase/tutorial/java/javaOO/classvars.html

Categories