0

Why is x not initialized in the following ?

public class rough {
    public static void main(String[] args) {
        int x;
        boolean found = false;
        for (int i = 0; i < 10; i++) {
            if (Math.random() < 0.5) {
                found = true;
                x = 10;
                break;
            }

        }
        if (!found)
            x = -1;
        System.out.println(x);//x isn't initialized here
    }
}

On average, for half of the iterations, the if inside the for loop would be true, thus initializing x. For the other half, found stays false therefore the outer if would initialize. Therefore, I don't understand why the compiler is annoyed.

As the ultimate distillation (see successive simplifications below), consider

public static void main(String[] args) {
        int x;
        boolean found = false;
        if (!found)
            x = -1;

        System.out.println(x);
    }

which also gives the error that x isn't init.

previous simplifications

Even more surprisingly, changing if (Math.random() < 0.5) to if(true) also has the same problem.

In fact, investigating further, replacing the original for loop by these

        for (int i=0;i<1;i++)
           x = 10;
 for (; !found; ) {
            x = 10;
            break;
        }

is equally worse. Only for(;;){... break;} & for(;true;){... break;} don't raise any init. errors.

lineage
  • 792
  • 1
  • 8
  • 20
  • 1
    The compiler doesn't go that far to get that far to understand that x value is linked to found boolean – azro Jun 12 '21 at 14:01
  • @azro thnx for the quick reply..I am actually using intellij IDE...I assumed it shows build errors after logically following through the code execution – lineage Jun 12 '21 at 14:03
  • Because you know type of input whereas compiler won't be certain that `x` will always have a value assigned to it. Hence it's warning you to initialize it before using it like `int x = 0; // or any value for that matter` – semicolon Jun 12 '21 at 14:03
  • 1
    @akuzminykh There is no code path that leads to `x` not being initialized, but the compiler isn't sentient and flow control detection using external `boolean` guard variables is a little too much for it to analyze (poor thing). – Elliott Frisch Jun 12 '21 at 14:06
  • Right. Knowing that 'x' is effectively set requires tracking that, if x is not set in the loop, then found remains false, and therefore x will get set after the loop. That;s a little much to ask for. – iggy Jun 12 '21 at 14:54

2 Answers2

2

The compiler can't easily detect all branches lead to x being initialized, but you can fix that (and the code) pretty easily by assigning -1 to x to begin with. Something like

public static void main(String[] args) {
    int x = -1;
    for (int i = 0; i < 10; i++) {
        if (Math.random() < 0.5) {
            x = 10;
            break;
        }
    }
    System.out.println(x);
}

And now you don't need found (so I removed it too).

Elliott Frisch
  • 198,278
  • 20
  • 158
  • 249
  • so in practise, x will always be initialized (in my code)? – lineage Jun 12 '21 at 14:10
  • 2
    This code is better not only because it avoids the compiler warning, but (more importantly in my view) it removes the need for an extra flag and for a test on that flag, making the code simpler, and therefore more obviously correct. The general technique might be described as 'initialize to the default value'. – iggy Jun 12 '21 at 14:57
  • 1
    @lineage - "in practise..." -- yes, it's a false warning in your case, but that's the price we pay for having compilers that attempt to find uninitialised-variable bugs for us. Sometimes you just have to supply an initialization you don't "need". In this case, though, there was a refactoring that was an improvement for other reasons. – iggy Jun 12 '21 at 15:01
  • @iggy that settled it. I needed the external flag for other reasons so the refactoring hadn't actually helped me. I came up with the question because I had always thought the compiler goes through *all* the logical conclusions that can be made about the code without running it...seems that isn't the case – lineage Jun 12 '21 at 15:06
  • @iggy in ElliotFrisch's words , *poor thing*. – lineage Jun 12 '21 at 15:12
  • @lineage - the compiler's flow analysis likely finds all paths through the code and checks for 'use without initialization', but cannot determine that certain paths can never be executed based on the run-time logic of that code. (Much hand-waving here) – iggy Jun 12 '21 at 15:14
  • @iggy see my simplified 'ultimate distillation' above..where's the runtime needed for that? – lineage Jun 12 '21 at 15:17
  • As written, `if (!found)` is code that checks, when the program is running, the value of some variable. There are two paths through that `if` statement, the case when `found` is true, and the case when `found` is false. More hand-wave: it's possible that later on in compilation it will be discovered that no code need to be generated for the test, but that's later. (Good job on the simplification, BTW) – iggy Jun 12 '21 at 15:22
1

(Writing this up as a separate answer since I think it'll benefit from being taken out of comments).

This is the language-lawyer's answer.

The language specification requires initialization of variables before they are used.

The rules include:

  1. The variable must be given a value on all possible paths through the code. The specification refers to this as 'definite assignment'.

  2. The compiler does not consider the values of expressions in this analysis. See Example 16.2 for this.

The second rule explains why even in cases that are 'obvious' to us, the compiler can't use that knowledge. Even if the compiler-writer cared to do a deeper analysis, adherence to the Java specification forbids it.

If the next question is 'but why?' then I'd have to guess, but the point of a standard is to get consistent behaviour. You don't want one compiler accepting as legal Java something that another compiler rejects.

iggy
  • 1,328
  • 4
  • 3