Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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.

like image 635
patrik1957 Avatar asked Jan 04 '19 15:01

patrik1957


People also ask

Can you use variables in a for loop?

Often the variable that controls a for loop is needed only for the purposes of the loop and is not used elsewhere. When this is the case, it is possible to declare the variable inside the initialization portion of the for.

Is it good to declare variable inside for loop in Java?

It's the result of JVM specifications... But in the name of best coding practice it is recommended to declare the variable in the smallest possible scope (in this example it is inside the loop, as this is the only place where the variable is used). It is the result of the JVM Soecification, not 'compiler optimization'.

Are variables in while loops local?

In Python, on the other hand, variables declared in if-statements, for-loop blocks, and while-loop blocks are not local variables, and stay in scope outside of the block.


2 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);
  }
}
like image 143
bruno Avatar answered Oct 10 '22 06:10

bruno


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.

like image 44
NeplatnyUdaj Avatar answered Oct 10 '22 05:10

NeplatnyUdaj