I am having problem with this code, i assigned a value to variable x in if block but compiler throws an error message. I don't get it why?
class psp
{
public static void main(String gg[])
{
int x, y;
y=5;
if(y==5)
{
x=5; // i have assigned a value to x
}
System.out.println(x);
}
}
Because y=5 Is executed at runtime so the compiler doesn't know the value at compile time.
The Java compiler does not take into account possible values of variables when deciding whether a certain execution path will take place. In this case, the compiler will not take into account that y is 5 in deciding whether the if condition will always be true and thus x will always be initialized.
The compiler will only see an if without an else and assume that there is a possibility that the if condition could be false.
You can remove this compiler error by initializing x outside of the if block or by including an else block and also initializing it there.
Related
Some may find it similar to the SO question Will Java Final variables have default values? but that answer doesn't completely solve this, as that question doesn't directly print the value of x within instance initializer block.
The problem arises when I try to print x directly inside the instance initializer block, while having assigned a value to x before the end of the block :
Case 1
class HelloWorld {
final int x;
{
System.out.println(x);
x = 7;
System.out.println(x);
}
HelloWorld() {
System.out.println("hi");
}
public static void main(String[] args) {
HelloWorld t = new HelloWorld();
}
}
This gives a compile time error stating that variable x might not have been initialized.
$ javac HelloWorld.java
HelloWorld.java:6: error: variable x might not have been initialized
System.out.println(x);
^
1 error
Case 2
Instead of directly printing, I am calling a function to print:
class HelloWorld {
final int x;
{
printX();
x = 7;
printX();
}
HelloWorld() {
System.out.println("hi");
}
void printX() {
System.out.println(x);
}
public static void main(String[] args) {
HelloWorld t = new HelloWorld();
}
}
This compiles correctly and gives output
0
7
hi
What is the conceptual difference between the two cases?
In the JLS, §8.3.3. Forward References During Field Initialization, its stated that there's a compile-time error when:
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.
The following rules come with a few examples, of which the closest to yours is this one:
class Z {
static int peek() { return j; }
static int i = peek();
static int j = 1;
}
class Test {
public static void main(String[] args) {
System.out.println(Z.i);
}
}
Accesses [to static or instance variables] by methods are not checked in this way, so the code above produces output 0, because the variable initializer for i uses the class method peek() to access the value of the variable j before j has been initialized by its variable initializer, at which point it still has its default value (§4.12.5 Initial Values of Variables).
So, to summarize, your second example compiles and executes fine, because the compiler does not check if the x variable was already initialized when you invoke printX() and when printX() actually takes place at Runtime, the x variable will be assigned with its default value (0).
Reading the JLS, the answer appears to be in section 16.2.2:
A blank final member field V is definitely assigned (and moreover is not definitely unassigned) before the block (§14.2) that is the body of any method in the scope of V and before the declaration of any class declared within the scope of V.
This means that when a method is called, the final field is assigned to its default value 0 before invoking it, so when you reference it inside the method, it compiles successfully and prints the value 0.
However, when you access the field outside of a method, it is considered unassigned, hence the compilation error. The following code will also not compile:
public class Main {
final int x;
{
method();
System.out.println(x);
x = 7;
}
void method() { }
public static void main(String[] args) { }
}
because:
V is [un]assigned before any other statement S of the block iff V is [un]assigned after the statement immediately preceding S in the block.
Since the final field x is unassigned before the method invocation, it is still unassigned after it.
This note in the JLS is also relevant:
Note that there are no rules that would allow us to conclude that V is definitely unassigned before the block that is the body of any constructor, method, instance initializer, or static initializer declared in C. We can informally conclude that V is not definitely unassigned before the block that is the body of any constructor, method, instance initializer, or static initializer declared in C, but there is no need for such a rule to be stated explicitly.
The difference is that in the first case you are calling System.out.println from initializer block so the block which is invoked before constructor. In the first line
System.out.println(x);
variable x is not yet initialized so that you get compilation error.
But in the second case you call instance method which doesn't know if variable has already been initialized so you don't have compilation error and you can see the default value for x
Ok, here is my 2 cents.
We all know that final variables can be initialized only While declaring or later on in constructors. Keeping that fact in mind, let see what happened here so far.
No errors Case:
So when you use inside a method, it have already a value.
1) If you initialize it, that value.
2) If not, the default value of data type.
Error case :
When you do that in an initialization block, which you are seeing errors.
If you look at the docs of initialization block
{
// whatever code is needed for initialization goes here
}
and
The Java compiler copies initializer blocks into every constructor. Therefore, this approach can be used to share a block of code between multiple constructors.
In compiler's eye, your code is literally equals to
class HelloWorld {
final int x;
HelloWorld() {
System.out.println(x); ------------ ERROR here obviously
x = 7;
System.out.println(x);
System.out.println("hi");
}
public static void main(String[] args) {
HelloWorld t = new HelloWorld();
}
}
You are using it before even initializing it.
Case 1 :
Gives you a compile-error,
Because at System.out.println(x);
you are trying to print x which was never initialized.
Case 2:
Works because you are not directly using any literal values, instead you are calling some method, which is correct.
General Rule is,
If you are trying to access any variable which is never initialized
then it will give a compilation error.
We deal here with initializer block. The Java compiler copies initializer blocks into every constructor.
The compiler error don't occure in second example, because printing x is in another Frame, please refer to spec.
I declared the variable outside the if and depending on various things assigned it within the if. The thing is, checking in debug mode showed that after it left the if block, the variable returned to being uninitialized for some reason.
The code looks something like this:
function() {
double x;
if (something happens) {
x = some_func();
}
}
some_func() works, and when I put a breakpoint within the if brackets, the x is shown with the correct value, yet once outside, it goes back to being uninitialized (as mentioned before). Any ideas what I'm doing wrong?
As per The pseudo-code posted by you all you need to do is that initialize the variable x at the declaration time or before printing as you are initializing the variable in a conditional statement and as we know that all the local variables need to be initialized before use in Java you get the variable uninitialized error.
For example we have :-
public class HelloWorld{
public static int check()
{
int c=10;
return c;
}
public static void main(String []args){
int a=1,x=5;
if (a==1) {
x = check();
}
System.out.println("hello World"+x);
}
}
in this example if you remove the initialization section of variable x from the above example you get the variable uninitialized error else it will work fine.
So simple solution to your problem keeping your pseudo code in mind is that you can initialize the variable x at the time of declaration.
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;
All code is in Java.
public class TestLocal
{
public static void main(String [] args)
{
int x;
if (args[0] != null)
{ // assume you know this will
// always be true
x = 7; // statement will run
}
int y = x; // the compiler will choke here
}
}
So, my Question is why does the compiler choke here? Does it bypass the if statement (This look quite horrible...) If I initialize x outside the if statement block then the compiler does not complain, as in this code:
public class TestLocal
{
public static void main(String [] args)
{
int x;
x = 7; // compiler is happy now..:)
if (args[0] != null)
{ // assume you know this will
// always be true
// statement will run
}
int y = x; // the compiler not complain here now.
}
}
Why such a horrible behavior from the compiler?
Java requires you to initialize the local variable as a safety measure. It stops you from accidently reading an odd value. If the compiler didn't complain and you were to read the value of x, it could be anything.
Well, the thing is that args[0] might be null, and then x would not be initialized before use. That is why the compiler is complaining at you.
When you don't pass argument then String[] array has nothing and is uninitialized. so the if condition will not be true in this case and even it will throw Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 0.. Now assume you have set args[0] to null value at some place in your code then the if(args[0]) != null) will evaluate to false . In that case you are assigning int y= x; where x is uninitialized. And I think you should know compiler takes these things into account. In java you can not use a local variable until it is initialized.
That's why the smart Java compiler gives you compile time error and tell you code properly.
:-). Hope you got it.
According to java Local variables must be initialized before use.
in your case you are trying to initialize it in condition.Conditions never guaranteed to executes(In run time it may executes or may not execute based on the condition) and trying to assign to y variable.
So compiler warns about it.
Because of performance issues among others, Java doesn't initialize automatically local variables. so here:
int x;
x still have an undefined value. Compiler won't allow you to compile the code because you are referring to x in this line:
int y = x;
It works in the second case because you are initializing x before it is referred elsewehere.
Granted, you are initializing x inside the if statement, but obviously it could be the case that your if evaluates to false, so the compiler takes that into account.
int y = x; // the compiler will choke here
Because in case your if block does not execute(when arg[0] ==null) then x will not be initialized. Java compiler mandates the variable to be initialized before they can be used.
You're declaring x as space to hold an integer, but since you didn't give it any integer to hold, it is still null.
The compiler looks at both the true and false paths of your if statement. If arg[0] contains something, then you're fine. Otherwise, when you try to set y = x, x has nothing to give to y and you get an error.
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.