Исключение указателя java

У меня есть некоторый класс, который создает корабль (класс Ship расширяет GameObject) и пытается добавить его в gameBoard.
Для этого он сообщает gameFrame добавить объект следующим образом:

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();
}

Затем GameFrame вызывает:

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

Который в свою очередь называет:

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);
    }
}

Теперь моя проблема в том, что я получаю исключение нулевого указателя, когда я gameFrame.addGameObject(myShip);
Сложность в том, что когда я запускаю через отладчик, я вообще не получаю NPE (но мой список объектов все еще кажется пустым).
Кроме того, я могу следить за каждым из них и по-прежнему видеть мой корабль, так что я просто неправильно ссылаюсь на свой GameObject (Ship)?
Должны ли мои параметры для addGameObject быть более абстрактными?

4 ответа

Решение

Проблема в том, что вы назначаете gameFrame внутри Runnable, который не запускается до позднего времени. Так gameFrame может быть нулевым в точке вызова gameFrame.addGameObject(myShip)

Возможно, проблема в используемом вами invokeLater, который запланирован всякий раз, когда Swing чувствует себя так, как это происходит, и, вероятно, происходит после gameFrame.addGameObject(myShip);

Этого не происходит в отладчике, потому что это отчасти гоночное состояние, в отладчике Swing вызывает метод выполнения перед gameFrame.addGameObject (myShip), потому что вы не можете щелкнуть достаточно быстро, чтобы предопределить этот оператор;)

Лучшее решение этой проблемы - переместить эти утверждения в run метод, как это:

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();
        }
    });
}

Делая это таким образом, 4 оператора будут выполняться в ожидаемом порядке, а атрибут не будет null когда требуется.

Проверьте область, где вы инициализировали эту переменную. т.е. фигурные скобки { } вокруг нового InvadersGameFrame(). Убедитесь, что вы используете объект после его создания

Другие вопросы по тегам