javanullpointerexceptionnullprogram-flow

Setting object to null and program flow in Java


I'd like a little help understanding how setting an object to null works in java. I have a situation where, seemingly, at first glance it appears that an object that is set to null, is suddenly not null, but obviously this can't be the case.

I have a class in which I create an object. This object is a scene. This is an Open GL ES 2.0 project, so this scene's render() and updateLogic() methods are called from onDrawFrame (this is controlled via a Scene Manager so we can easily switch scenes).

So, I might have something like this (code cut down for the purpose of the question):

public class MyGLRenderer implements GLSurfaceView.Renderer{

    MyScene myScene;
    SomeOtherScene someOtherScene;

    public void createScenes(){
        myScene = new MyScene(this);
        someOtherScene = new SomeOtherScene(this);
        SceneManager.getInstance().setCurrentScene(myScene);
    }

    public void cleanUp(){
        myScene = null;
        Log.v("tag","myScene (from MyGLRenderer) is: "+myScene);
        SceneManager.getInstance().setCurrentScene(someOtherScene);   //Scene changed but this won't take effect until the next 'tick'   
    } 

    @Override
    public void onDrawFrame(GL10 gl) {
        SceneManager.getInstance().getCurrentScene().updateLogic();
        SceneManager.getInstance().getCurrentScene().render();
    }

}

In the above situation, processing is turned over to myScene which would look something like this:

public class MyScene implements Scene{

    MyGLRenderer renderer;

    public myScene(MyGLRenderer renderer){     
        this.renderer = renderer;
    }

    @Override
    public void render(){
        //Render something here
    }

    @Override
    public void updateLogic(){
        doSomething();           
        //The condition here could be anything - maybe the user taps the sceen and a flag is set in onTouchEvent for example
        if (someConditionIsMet){
            renderer.cleanup();
        }            
         Log.v("tag","myScene (from within myScene) is: "+this);
    }
}

So, when I set the scene using my scene manager, processing is turned over to that scene and it's updateLogic and render methods get called from onDrawFrame continuously.

When I ran my code, I was suprised it didn't crash with a NullpointerException. The logs were like this:

myScene (from within myScene) is: com.program.name.MyScene@26354632
myScene (from within myScene) is: com.program.name.MyScene@26354632
myScene (from within myScene) is: com.program.name.MyScene@26354632
myScene (from within myScene) is: com.program.name.MyScene@26354632
myScene (from within myScene) is: com.program.name.MyScene@26354632
myScene (from within myScene) is: com.program.name.MyScene@26354632
myScene (from within myScene) is: com.program.name.MyScene@26354632
myScene (from within myScene) is: com.program.name.MyScene@26354632
myScene (from MyGLRenderer) is: null
myScene (from within myScene) is: com.program.name.MyScene@26354632

As you can see, 'myScene' is valid up until the cleanUp() method is called and sets it to null. But the code then returns to myScene to finish off, where it's still valid (not null).

I'd really like to understand how thing works in Java - why does it seem to be null one minute (or from one place) and then not (from a different place)?


Solution

  • You just clear out the reference to MyScene in renderer, but the object is still there and SceneManager probably still holds on to it.

    The confusion seems to be about "setting an object to null". That's not really possible. You can only set variables pointing to objects to null. After this, the variable points to null, but the object may still be there (as in your case).

    Specificially, you hand it over to the scene manager here:

    SceneManager.getInstance().setCurrentScene(myScene);
    

    The object will get garbage collected only if nothing holds a reference to it. In your case, this is probably when the scene change takes effect.