14

I came across a behavior, I didn't knew of earlier, in the follow up code.

Consider the 1st case:

public static void main(String[] args) {    
    final String str = null;
    System.out.println(str.length());  // Compiler Warning: NullPointerAccess
}

As expected, the compiler shows me following warning on str being null - Null pointer access: The variable str can only be null at this location.

Now, when I moved that variable a static final field initialized to null:

class Demo {
    static final String str = null;

    public static void main(String[] args) {
        System.out.println(str.length());  // No Compiler Warning
    }
}

Now, compiler doesn't show any warning. AFAIK, Compiler should know that str being final, will not change its value, at any point of the code. And given that it is null, will definitely result in NullPointerException later on, which it does.

Although, compiler successfully warns me of this in the first case, why it cannot identify this in the 2nd case. Why this change of behaviour? The behaviour is same, if I change static field to instance field, and access it using an instance of Demo.

I thought this behaviour might have been specified in JLS, so I went through the topic Definite Assignment, but didn't find anything related to this issue. Can anyone explain the change in behaviour? I'm looking for some strong point with some link to JLS if possible?

Apart from that, why compiler shows me only warning in the first place, as I think for the same reason I stated above, the method invocation will definitely throw NPE at runtime, since the field can't be changed? Why didn't it rather show me a Compiler Error? Am I expecting too much from compiler, as it seems to be quite obvious, that the runtime result of str.length() can't be anything than NPE?


Sorry for missing that earlier:

I'm using Eclipse Juno, on Ubuntu 12.04 with OpenJDK 7.

Roman C
  • 49,761
  • 33
  • 66
  • 176
Rohit Jain
  • 209,639
  • 45
  • 409
  • 525

3 Answers3

2

I am not sure in 100%, but in the second case where you have final field against local variable, there is possible assigns to this final field some value direct in static (or instance block depending if variable is static or not) initialization block:

class Demo {
...
static {
 str = "some text";
}
...
}

so compiler don't give you warn.

michal
  • 1,796
  • 1
  • 13
  • 11
2

Wow! It turned out, it was eclipse specific issue. When I compiled the code using:

javac -Xlint:all Demo.java

it didn't show any warning for any case. So, I went back to eclipse to check any settings enabled for this case, and found one.

In Windows -> Preferences -> Java -> Compiler -> Errors/Warnings, under Null Analysis, I can change the way the Null Pointer Access should be treated by Eclipse Compiler. I can set it to - Ignore, Error, or Warning.

And now it seems like a completely silly question. Shame on me. :(

Rohit Jain
  • 209,639
  • 45
  • 409
  • 525
1
public static void main(String[] args) {    
    final String str = null;
    System.out.println(str.length());  // Compiler Warning: NullPointerAccess
}

In this case str is a local variable and compiler will not compile if you try to perform any operations on it before initializing it. Flow analyzing is altogether different, it will check the flow of the code in which it detect that at the point you perform the length() operation the local variable str can only be null.

class Demo {
    static final String str = null;

    public static void main(String[] args) {
        System.out.println(str.length());  // No Compiler Warning
    }
}

In this case str is an instance variable and will be null even if you don't explicitly assign so.

Still why no warning here?

You could have initialize the instance variable in the constructor. Or you could call a setter method on it before calling the lenght() operation on it. So it escapes from the flow analysis(compiler is not sure whether the instance variable will be null at that point or not but in 1st case compile is sure that the local variable will always be null).

Aniket Thakur
  • 66,731
  • 38
  • 279
  • 289
  • I would have buyed it in case of non-final fields, in which case, the behaviour is bound to be different in two cases. But given both are final variables, none of them can be changed after initialization. So, I cannot see how compiler cannot identify null access in one case, while it can in the other. – Rohit Jain Aug 02 '13 at 13:22
  • 1
    I think you didn't notice that the field is `final static`. You can't initialize it in constructor, neither in any setter? – Rohit Jain Aug 02 '13 at 13:28
  • Yeah did not see final there! I don't know exactly how flow detection works in java but I would guess it does not handle your case. If it would not have been static and just final it could be initialized in the constructor and if it would have been just static then in some static block. Your case is unique and I guess it is not handled. – Aniket Thakur Aug 02 '13 at 14:56