No exception raised for referenced unassigned array element containing an object

Tags: , ,



MRP

class B {
    static int v;
    
    public B(int i) {
        System.out.format("Constructor called with value %dn", i);
        v=i;
    }
}

public class A {
    static B[] c;
    
    A(){
        c=new B[5];
        
        c[1]=new B(1);
    
        for (int i=0; i<3; i++) {
            System.out.format("c[%d] is %dn", i, c[i].v);
        }
        
        c[2]=new B(2);
        
        for (int i=0; i<3; i++) {
            System.out.format("c[%d] is %dn", i, c[i].v);
        }
    }
    
    public static void main(String[] args) {
        new A();
    }
}

Output is:

Constructor called with value 1
c[0] is 1
c[1] is 1
c[2] is 1
Constructor called with value 2
c[0] is 2
c[1] is 2
c[2] is 2

Would expect exception to be raised by reference to unassigned array elements e.g. c[0]. Array values are also incorrectly changed by previous assignment. c[0] is never assigned a value but takes on the values 1 and 2 in the output above.

public class A {
    static String[] c;
    
    A(){
        c=new String[5];
        
        c[0]=new String("alpha");
    
        for (int i=0; i<3; i++) {
            System.out.format("c[%d] is %sn", i, c[i]);
        }
        
        c[1]=new String("beta");
        
        for (int i=0; i<3; i++) {
            System.out.format("c[%d] is %sn", i, c[i]);
        }
    }
    
    public static void main(String[] args) {
        new A();
    }
}


Output for the above is:

c[0] is alpha
c[1] is null
c[2] is null
c[0] is alpha
c[1] is beta
c[2] is null

Different behavior is seen for String object in the above example.

Answer

So the question is why doesn’t c[i].v result in a NullPointerException when c[i] is null.

Lets start with something slightly simpler:

B b = null;
System.out.println(b.v);

This will not throw a NullPointerException.

Why?

Since the v field of B is static, we do not need to dereference the value of b to fetch the value of v. The value of v is not associated with any specific instance of B.

So, in fact, b.v and B.v are equivalent.


In the more general case, consider that <expr> is some expression whose static type is B. Then:

  V v = <expr>.v

has the same effect as:

  B temp = <expr>;
  V v = B.v;

In other words, the expression is evaluated and its value is discarded. Then the value of the static field is taken. But since the temp is not dereferenced (because it doesn’t need to be), there will be no NPE in the case that the expression evaluates to zero … as it does in your example.


The difference in your string example is that you are printing the state of a String instance rather than the state of a static field. And no NPE occurs in the string concatenation because the + operator maps the null to "null" rather than calling null.toString().


The bottom line here is that using an instance reference to access a static field is a bad idea. Because the syntax doesn’t do what you might expect it to do.

Indeed some Java style checkers / static analyzers will flag that as bad style or a possible bug.



Source: stackoverflow