This fails to compile (with an illegal forward reference error), as one would expect:
class test {
int x = x + 42;
}
But this works:
class test {
int x = this.x + 42;
}
What's going on? What gets assigned in the latter case?
It is too difficult to discover and forbid all accesses to x during x's initialization. For example
int x = that().x; | int x = getX();
|
Test that(){ return this; } | int getX(){ return x; }
The spec stops at "access by simple name" and does not try to be more comprehensive.
In another section, "Definite Assignment", the spec does the similar thing. For example
public class Test
{
static final int y;
static final int z = y; // fail, y is not definitely assigned
static{ y = 1; }
}
public class Test
{
static final int y;
static final int z = Test.y; // pass... because it's not a simple name
static{ y = 1; }
}
Interestingly, "Definite Assignment" specifically mentions that this.x is equivalent to x
(or, for a field, the simple name of the field qualified by this)
this clause could be added to the section quoted by NPE as well.
the usage is via a simple name (or a simple name qualified by this)
But in the end, it is impossible at compile time to analyze all possible usages/accesses to a field.
Summary: Both initializers access a field that's yet to be initialized (and therefore still has the default value of zero). Since this is likely to be a programming error, the language bans some simple forms of such access. However, it does not ban more complex form.
The behaviour is compliant with the JLS, specifically §8.3.2.3. Restrictions on the use of Fields during Initialization
The declaration of a member needs to appear textually 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.
The usage is via a simple name.
C is the innermost class or interface enclosing the usage.
The first example satisfies all four conditions and is therefore invalid. The second example doesn't satisfy the third condition (this.x is not a simple name), and is therefore OK.
The overall sequence of events is as follows:
When an instance of a class is created, all fields are initialized to their type's default values.
Initializers are then run in textual order (from top to bottom).
Thus if an initializer refers to a field that appears later in the class definition (or to the field itself), it would see the default value of that other field. This is likely to be be a programming error and is therefore explicitly forbidden by §8.3.2.3.
If you circumvent §8.3.2.3 by, for example, using this. to forward-refer to a field, you'll see the default value (zero for int). Thus the following is well-defined and is guaranteed to set x to 42:
class test {
int x = this.x + 42;
}
In the first case compiler tries to evaluate expression 'x + 42' but fails because x is not initialized.
In the second case expression 'this.x + 42' is evaluated at runtime (because of 'this' keyword), when x is already initialized and has value 0.
Related
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.
There are several similar questions on SO about method reference to local class constructor, but I'd like to clarify slightly other thing. Consider following piece of code:
static Callable gen(int i) {
class X {
int x = i;
public String toString() { return "" + x; }
}
return X::new;
}
...
System.out.println(gen(0).call());
System.out.println(gen(1).call());
Obviously this will printout
0
1
It turns out, that X class has constructor of the form ...$X(int) (you can find it via X.class.getDeclaredConstructors()).
But what is interesting here, is that returned lambdas (or method references) aren't simple reference to constructor ...$X(int) like, for example, Integer::new. They internally invoke this constructor ...$X(int) with predefined argument (0 or 1).
So, I'm not sure, but looks like this kind of method reference is not precisely described in JLS. And there is not other way except this case for local classes, to produce such kind of lambdas (with predefined constructor arguments). Who can help clarify this?
To be precise:
where is in JLS such kind of method reference described?
is any other way to create such method reference to arbitrary class constructor with predefined arguments?
You are focusing too much on irrelevant low level details. On the byte code level, there might be a constructor accepting an int parameter, but on the language level, you didn’t specify an explicit constructor, hence, there will be a default constructor without any arguments, as with any other class.
This should become clear when you write the pre-Java 8 code:
static Callable<Object> gen(int i) {
class X {
int x = i;
public String toString() { return "" + x; }
}
X x=new X();
…
You instantiate X by its default constructor, not taking any arguments. Your local class captures the value of i, but how it does so on the low level, i.e. that X’ constructor has a synthetic int parameter and the new expression will pass the value of i to it, is an implementation detail.
You can even add an explicit constructor as
X() {}
without changing anything.
Obviously, you can also write the expression new X() inside a lambda expression here, as expressions don’t change their semantic when being placed inside a lambda expression:
return () -> new X();
or use it’s short-hand form, the method reference
return X::new;
There is nothing special about it, the behavior is understandable even without referring to the specification, if you forget about the distracting low level details. X may capture as many local variables as you like, the constructor’s number of parameters doesn’t change (on the language level).
This behaviour is defined in the JLS section §15.13.3:
If the form is ClassType :: [TypeArguments] new, the body of the invocation method has the effect of a class instance creation expression of the form new [TypeArguments] ClassType(A1, ..., An), where the arguments A1, ..., An are the formal parameters of the invocation method, and where:
The enclosing instance for the new object, if any, is derived from the site of the method reference expression, as specified in §15.9.2.
The constructor to invoke is the constructor that corresponds to the compile-time declaration of the method reference (§15.13.1).
Although this talks about enclosing instances, captured variables and parameters are not mentioned in §15.13.3.
As for your second question, you need to manually capture and change the parameter:
static Callable gen(int i) {
final int i1 = someCondition() ? i : 42;
class X {
int x = i1; // <-
public String toString() { return "" + x; }
}
return X::new;
}
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.
This code compiles (using Java 8), giving the warning The assignment to variable value has no effect:
package test;
public class Test {
private static int value;
static {
value = Test.value;
}
public static void main(String[] args) {
System.out.println(value);
}
}
The program outputs 0 as expected. But why does the compiler allow this, what is the use case?
Because variables in Java have a default value which is initialized before any other initialization, unless the initialization happens directly when declaring the variable. Indeed value is 0.
The warning seems reasonable, assigning a variable to itself has no apparent effect but I don't think it should be considered a compile type error since it's syntactically and semantically correct.
If as variable is assigned to itself, the compiler issues a warning, as in the following:
int x = 2;
x = x;
This warning is usually a sign of a programming problem, so for itself it is a good thing to have it, yet in the case that the variable is volatile, which will be the ONLIEST useful Use Case I can think off, the warning is incorrect.
int y = 0;
volatile int x = 2;
y = 3;
x = x;
The java memory model enforces that every thread reading x and y in that order is guaranteed to see the value 3 for y. This is because assigning to x in one thread and reading x in the other establishes a happens-before-ordering.
The assignment has no effect to the value of the variable but it does have a huge effect for concurrency.
Note that in the example above the warning can be suppressed by using
x = x + 0;
Yet there is no such hack for references. One Use Case that will hit you is when trying to sync the content of a int[]. One thread reads a member:
myarray[3]
and another thread writes the member:
myarray[3] = 2;
Because array members themselfes cannot be volatile, you have to make myarray volatile and add the following to the writing thread:
myarrray = myarray;
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