6

Here in following code we are getting value of i on a null reference, though a NPE is not there.

public class Test {
    static int i = 10;

    Test getTest() {
        return null;    
    }

    public static void main(String args[]) {
        Test t = new Test();
        System.out.println(t.getTest());  
        System.out.println(t.getTest().i);
    }
}

output

null
10
Not a bug
  • 4,286
  • 2
  • 40
  • 80
eatSleepCode
  • 4,427
  • 7
  • 44
  • 93
  • Check it here : http://stackoverflow.com/q/21037406/1686291 – Not a bug Jan 21 '14 at 10:41
  • Since this can be a confusing scenario, it will actually generate a compiler warning in Eclipse: "Non-static access to a static member." – Medo42 Jan 21 '14 at 10:53

8 Answers8

7

Informally speaking, you can think of this

System.out.println(t.getTest().i);

as being equivalent to

System.out.println(Test.i);

because i is static.

This is the simplest answer probably.

Strictly speaking, they are not equivalent. Actually
getTest() is called but its return value is not used
for accessing the i field as this test below shows.

public class Test {
    static int i = 10;

    Test getTest() {
        System.out.println("Method getTest() called!");
        return null;    
    }

    public static void main(String args[]) {
        Test t = new Test();
        System.out.println(t.getTest());  
        System.out.println(t.getTest().i);
    }
}
peter.petrov
  • 38,363
  • 16
  • 94
  • 159
  • indeed.. The only logical answer is that.. But the compiler should do this during compilation itself... On finding i, it should replace t.getTest() with Test. Otherwise if t.getTest() is executed, then it returns null which cannot be used to access i... correct me if i am wrong... – TheLostMind Jan 21 '14 at 10:47
  • see my answer, the two are not equivalent, although both access i in a static way – Silly Freak Jan 21 '14 at 10:48
  • `t.getTest()` will be executed, but the result will not be used in any way. – Medo42 Jan 21 '14 at 10:49
4

i being a static variable, no instance is needed to get its value.

Maurice Perry
  • 32,610
  • 9
  • 70
  • 97
4

The generated bytecode looks like this:

18  getstatic java.lang.System.out : java.io.PrintStream [24]
21  aload_1 [t]
22  invokevirtual experiments.Experiments.getTest() : experiments.Experiments [30]
25  pop
26  getstatic experiments.Experiments.i : int [10]
29  invokevirtual java.io.PrintStream.println(int) : void [38]

As you see, t.getTest() is indeed called (21,22), but its result not used (25). The field i is accessed in a static way (26). Java Bytecode has no way of accessing static members via an instance. Note that this means that t.getTest().i and Test.i are not equivalent expressions! In a hypothetical syntax, the equivalent could look like this (using the semantics I'd find intuitive for this syntax:

System.out.println( {t.getTest(); return Test.i;} );

Note that the same holds for a field test: t.test.i is different from Test.i. Although getting the field can't have any side effects and is not a valid statement on its own, the field access could still be advised by AspectJ or something similar.

Silly Freak
  • 4,061
  • 1
  • 36
  • 58
  • Are you sure that the getTest() being called is not for the first t.getTest() ? – TheLostMind Jan 21 '14 at 10:51
  • entirely. 18 is the second `getstatic java.lang.System.out` in the bytecode, and the line numbers tell me the same. Besides that, the lines in the bytecode make perfect sense for coding the second `println`. I can clarify it if you want – Silly Freak Jan 21 '14 at 10:58
  • No need...I trust you... :) – TheLostMind Jan 21 '14 at 11:00
  • @SillyFreak "*Note that this means that `t.getTest().i` and `Test.i` are not equivalent expressions!*" - can you expand it? It's very interesting. – Eel Lee Jan 21 '14 at 11:12
  • they are equivalent in value, but not in the code executed. As I stated, `t.getTest().i` does execute `t.getTest()`, while `Test.i` obviously doesn't – Silly Freak Jan 21 '14 at 11:16
  • @SillyFreak but then how would it work? I'm not really familiar with the bytecode, however if `t.getTest().i` does execute `t.getTest()`, then it's resolved to the Test object containing `null` value. So the question remains - how `null.i` can work? – Eel Lee Jan 21 '14 at 11:40
  • The compiler doesn't generate anything of the `null.i` sort. It recognizes that `t.getText()` is declared to return `Test`, and that `i` is static. thus, it generates a static access `Test.i` in addition to calling `t.getText()`. if it were legal, you would write `System.out.println( {t.getTest(); return Test.i;} );` as a truly equivalent statement, at least if you interpret this in the way I find intuitive. – Silly Freak Jan 21 '14 at 11:47
  • @SillyFreak thank you for the answer. Consider this adding to your original answer, I think it's a valuable addition! – Eel Lee Jan 21 '14 at 12:34
2
 Test t = new Test(); // initialize t

Here t isn't null as it has been initialized. Thus, you do not get a NullPointerException.

In next case where you expected a NullPointerException since t.getTest() returned null,

t.getTest().i;

i is a static variable and you do not need to an instance to access static variables, you can just access them directly. Thus, you do not get NullPointerException here too.

And moreover,

System.out.println(i); // is an another way to access static i
Rahul
  • 44,383
  • 11
  • 84
  • 103
Ruchira Gayan Ranaweera
  • 34,993
  • 17
  • 75
  • 115
2

From Java Language Specifications

Receiver Variable Is Irrelevant For static Field Access

The following program demonstrates that a null reference may be used to access a class (static) variable without causing an exception:

class Test3 {
    static String mountain = "Chocorua";
    static Test3 favorite(){
        System.out.print("Mount ");
        return null;
    }
    public static void main(String[] args) {
        System.out.println(favorite().mountain);
    }
}

It compiles, executes, and prints:

Mount Chocorua

Even though the result of favorite() is null, a NullPointerException is not thrown. That "Mount " is printed demonstrates that the Primary expression is indeed fully evaluated at run time, despite the fact that only its type, not its value, is used to determine which field to access (because the field mountain is static).

Implies even though primary expression (Here is instance) is evaluated at run time, but its value is discarded and only its type is considered.

Prateek
  • 12,014
  • 12
  • 60
  • 81
1

Static methods or variables does not need a reference to the object. You can call it even reference to the object is null.

linkamp
  • 215
  • 2
  • 5
1

To be more specific,

While accessing the Static variable, compiler will generate an getStatic instruction corresponding to that static and it will be used to access that static. thus static are instance independent they are resolved by the field/method using just an index to the run time constant pool that will be later used to solve the field reference location..

For more details refer this SO answer : https://stackoverflow.com/a/21047440/1686291

Community
  • 1
  • 1
Not a bug
  • 4,286
  • 2
  • 40
  • 80
0

Simply speaking, compiler takes statics from class def, not from an object. That is why you can replace t.getTest().i with Test.i and it will be the same.

Mr. P
  • 1,387
  • 1
  • 11
  • 25