java Null Pointer Exception passing object

2019-08-03 07:08发布

问题:

I have some class which creates a Ship (class Ship extends GameObject) and attempts to add it to a gameBoard.
To do this, it tells the gameFrame to add the object as follows:

public void startNewGame() {
    Ship myShip = new Ship(GAME_BOARD_WIDTH / 2, GAME_BOARD_HEIGHT-1, SHIP_WIDTH,
            SHIP_HEIGHT);
    SwingUtilities.invokeLater(new Runnable() {
        public void run() {
            gameFrame = new InvadersGameFrame();
        }
    });
    gameFrame.addGameObject(myShip); //Problem line
    gameFrame.repaint();
}

The gameFrame then calls:

GameBoard gameBoard = new GameBoard();
...
...
public void addGameObject(GameObject ob) {
    gameBoard.addGameObject(ob);
}

Which in turn calls:

public class GameBoard extends JPanel implements GameData{
    private JPanel gameBoard;
    private List<GameObject> objects = new ArrayList<>();

    public GameBoard() {
        gameBoard = new JPanel();
    }

    @Override
    protected void paintComponent(Graphics g)
    {
        super.paintComponent(g);
        setBackground(Color.black);
        g.setColor(Color.RED);
        for(GameObject ob : objects){
            g.drawOval(ob.x, ob.y, ob.width, ob.height);
        }
    }

    //Places object into list for drawing upon next repaint.
    public void addGameObject(GameObject ob) {
        objects.add(ob);
    }
}

Now my problem is that I receive a Null Pointer Exception when I gameFrame.addGameObject(myShip);
The tricky thing is when I run via debugger I do not receive the NPE at all (but my objects list still seems empty).
Also, I can follow into each of these and still see myShip, so am I just referencing my GameObject (Ship) wrong?
Should my parameters for addGameObject somehow be more abstract?

回答1:

The problem is that you are assigning gameFrame inside of the Runnable, which does not get run until later. So gameFrame may be null at the point you call gameFrame.addGameObject(myShip)



回答2:

The problem probably is the invokeLater you use, that is scheduled whenever Swing feels like it and probably happens after gameFrame.addGameObject(myShip);

This doesnt happen in the debugger because it is somewhat of a race condition, in the debugger Swing invokes the runmethod before gameFrame.addGameObject(myShip) because you can't click fast enough to preemt that statement ;)



回答3:

The best solution to this problem is to move those statements inside the run method, like this:

public void startNewGame() {
    SwingUtilities.invokeLater(new Runnable() {
        public void run() {
            Ship myShip = new Ship(GAME_BOARD_WIDTH / 2, GAME_BOARD_HEIGHT-1, 
                SHIP_WIDTH, SHIP_HEIGHT);
            gameFrame = new InvadersGameFrame();
            gameFrame.addGameObject(myShip); //Problem line
            gameFrame.repaint();
        }
    });
}

Doing it this way, the 4 statements will be run in the expected order, and the attribute will not be null when required.



回答4:

check the scope where you initialized that variable. i.e. the curly braces { } around the new InvadersGameFrame(). Make sure you are using the object after creating it