How do I solve local referenced variables inside a for loop?

Viewed 80

I'm writing a Swing application and trying to make a menu where each menu item has its own action:

Here's how I wanted to solve this:

private void createGameLevelMenuItems(JMenu menu){
    for (int i = 0; i<10; i++) {
        JMenuItem item = new JMenuItem(new AbstractAction("Level-" + i) {
            @Override
            public void actionPerformed(ActionEvent e) {
                game.loadGame(i);
                board.refresh();
                pack();
            }
        });
        menu.add(item);
    }
}

However, I cannot use loadGame(i), because it says i would have to be final. I understand the reason for this, but I do not know how to work my way around it.

3 Answers

Quick trick: define a final variable at each iteration of the loop that takes the (non final) value of i and use it:

private void createGameLevelMenuItems(JMenu menu){

  for (int i = 0; i<10; i++) {
    final int j = i;   // <--- this line do the thing
    JMenuItem item = new JMenuItem(new AbstractAction("Level-" + j) {
        @Override
        public void actionPerformed(ActionEvent e) {
            game.loadGame(j);
            board.refresh();
            pack();
        }
    });
    menu.add(item);
  }
}

To add an example to my comment above. You could just create a class implementing AbstractAction, store the i in instance variable and provide it via constructor:

private void createGameLevelMenuItems(JMenu menu){
    for (int i = 0; i<10; i++) {
        JMenuItem item = new JMenuItem(new LoadAction(i));
        menu.add(item);
    }
}

private class LoadAction extends AbstractAction {
    private int i;

    public LoadAction(int i) {
        super("Level-" + i);
        this.i = i;
    }

    @Override
    public void actionPerformed(ActionEvent e) {
        game.loadGame(i);
        board.refresh();
        pack();
    }
};

This assumes the game and board are final variables in the encapsulating class, but since you have just a problem with i, I guess it's the case.

Yes, local variables within a function must be final. When I come to this problem, I just define a dummy Variable inside the for loop:

for (int i=0;i<10;i++) {
    int useI = i;
    JMenuItem item = new JMenuItem(new AbstractAction("Level-" + i) {
        @Override
        public void actionPerformed(ActionEvent e) {
            game.loadGame(useI);
            board.refresh();
            pack();
        }
    });
}
Related