Fabian Röling
Fabian Röling

Reputation: 1356

How to tell Java that a variable cannot possibly be null?

I have a program that basically looks like this:

boolean[] stuffNThings;
int state=1;
for(String string:list){
   switch(state){
      case 1:
         if(/*condition*/){
            // foo
            break;
         }else{
            stuffNThings=new boolean[/*size*/];
            state=2;
         }
      // intentional fallthrough
      case 2:
         // bar
         stuffNThings[0]=true;
   }
}

As you, a human, can see, case 2 only ever happens when there was previously a state 1 and it switched to state 2 after initialising the array. But Eclipse and the Java compiler don't see this, because it looks like pretty complex logic to them. So Eclipse complains:

The local variable stuffNThings may not have been initialized."

And if I change "boolean[] stuffNThings;" to "boolean[] stuffNThings=null;", it switches to this error message:

Potential null pointer access: The variable stuffNThings may be null at this location.

I also can't initialise it at the top, because the size of the array is only determined after the final loop in state 1.

Java thinks that the array could be null there, but I know that it can't. Is there some way to tell Java this? Or am I definitely forced to put a useless null check around it? Adding that makes the code harder to understand, because it looks like there may be a case where the value doesn't actually get set to true.

Upvotes: 1

Views: 1408

Answers (4)

Stephen C
Stephen C

Reputation: 718836

Java thinks that the array could be null there, but I know that it can't.

Strictly speaking, Java thinks that the variable could be uninitialized. If it is not definitely initialized, the value should not be observable.

(Whether the variable is silently initialized to null or left in an indeterminate state is an implementation detail. The point is, the language says you shouldn't be allowed to see the value.)

But anyway, the solution is to initialize it to null. It is redundant, but there is no way to tell Java to "just trust me, it will be initialized".


In the variations where you are getting "Potential null pointer access" messages:

  1. It is a warning, not an error.
  2. You can ignore or suppress a warning. (If your correctness analysis is wrong then you may get NPE's as a result. But that's your choice.)
  3. You can turn off some or all warnings with compiler switches.
  4. You can suppress a specific warning with a @SuppressWarnings annotation:

    • For Eclipse, use @SuppressWarnings("null").
    • For Android, use @SuppressWarnings("ConstantConditions").

      Unfortunately, the warning tags are not fully standardized. However, a compiler should silently ignore a @SuppressWarnings for a warning tag that it doesn't recognize.

  5. You may be able to restructure the code.

In your example, the code is using switch drop through. People seldom do that because it leads to code that is hard to understand. So, I'm not surprised that you can find edge-case examples involving drop-through where a compiler gets the NPE warnings a bit wrong.

Either way, you can easily avoid the need to do drop-through by restructuring your code. Copy the code in the case 2: case to the end of the case 1: case. Fixed. Move on.


Note the "possibly uninitialized" error is not the Java compiler being "stupid". There is a whole chapter of the JLS on the rules for definite assignment, etcetera. A Java compiler is not permitted to be smart about it, because that would mean that the same Java code would be legal or not legal, depending on the compiler implementation. That would be bad for code portability.

What we actually have here is a language design compromise. The language stops you from using variables that are (really) not initialized. But to do this, the "dumb" compiler must sometimes stop you using variables that you (the smart programmer) know will be initialized ... because the rules say that it should.

(The alternatives are worse: either no compile-time checks for uninitialized variables leading to hard crashes in unpredictable places, or checks that are different for different compilers.)

Upvotes: 5

Joop Eggen
Joop Eggen

Reputation: 109557

More understandable would be:

    boolean[] stuffNThings;
    boolean initialized = false;
    for (String string: list) {
        if (!initialized) {
            if (!/*condition*/) {
                stuffNThings = new boolean[/*size*/];
                initailized = true;
            }
        }
        if (initialized) {
            // bar
            stuffNThings[0] = true;
        }
    }

Two loops, one for the initialisation, and one for playing with the stuff might or might not be more clear.

It is easier on flow analysis (compared to a switch with fall-through).

Furthermore instead of a boolean[] a BitSet might used too (as it is not fixed sized as an array).

BitSet stuffNThings = new BitSet(/*max size*/);

Upvotes: 0

Fabian Röling
Fabian Röling

Reputation: 1356

After just trying to execute the code regardless of Eclipse complaining, I noticed that it does indeed run without problems. So apparently it was just a warning being set to "error" level, despite not being critical.
There was a "configure problem severity" button, so I set the severity of "Potential null pointer access" to "warning" (and adjusted some other levels accordingly). Now Eclipse just marks it as warning and executes the code without complaining.

Upvotes: 0

GhostCat
GhostCat

Reputation: 140457

A distinct non-answer: when code is "so" complicated that an IDE / java compiler doesn't "see it", then that is a good indication that your code is too complicated anyway. At least for me, it wasn't obvious what you said. I had to read up and down repeatedly to convince myself that the statement given in the question is correct.

You have an if in a switch in a for. Clean code, and "single layer of abstraction" would tell you: not a good starting point.

Look at your code. What you have there is a state machine in disguise. Ask yourself whether it would be worth to refactor this on larger scale, for example by turning it into an explicit state machine of some sort.

Another less intrusive idea: use a List instead of an array. Then you can simply create an empty list, and add elements to that as needed.

Upvotes: 0

Related Questions