Local variable access to inner class needs to be d

2019-01-14 19:01发布

I got a problem of local variable access to inner class need to be declared final. It is from method createGrids() -> "squares[i][j] = 0;" that i is a local variable that need to be declared final. I don't know why and I have added final in fields but it is not working as well.

import java.util.ArrayList;
import java.util.Random;

//omitted

public class Minesweeper{
    private JFrame frame;
    private int cols = 9;
    private int rows = 9;
    public static final int GRID_HEIGHT = 9;
    public static final int GRID_WIDTH = 9;
    final JButton[][] grids = new JButton[GRID_WIDTH][GRID_HEIGHT];
    final int [][] squares = new int [GRID_WIDTH][GRID_HEIGHT];
    private static int width = 500;
    private static int heigth = 400;

    private JPanel s;
    private JPanel n;
    private JPanel w;
    private int mines = 10;
    private int bomb = 1;
    private JLabel j1;
    private JPanel e;
    private JRadioButton moreGrid;
    ArrayList<Integer> list = new ArrayList<Integer>();

    public Minesweeper() {
        mines=10;
        createGrids();
        s = new JPanel();
        n = new JPanel();
        e = new JPanel();
        w = new JPanel();

        resetButton = new JButton("Rest");
        resetButton.addActionListener(new ActionListener(){
                public void actionPerformed(ActionEvent e){ createGrids();}
            });
        newGameButton = new JButton("New Game");
        frame.add(n, BorderLayout.NORTH);
        frame.add(w, BorderLayout.WEST);
        frame.add(s, BorderLayout.SOUTH);
        s.add(resetButton);
        s.add(newGameButton);
    }

    public void game()
    {
        for(int i = 0; i < GRID_WIDTH; i++) {
            for(int j = 0; j < GRID_HEIGHT; j++) {
                squares[i][j] = 0;
            }
        }
    }
    public void setRandom()
    {
        Random r = new Random();
        for(int x = 0; x < mines; x++){
            int b = r.nextInt(9);
            int c = r.nextInt(9) ;   
            squares[b][c] = bomb;   
        }
    }

    public void createGrids(){
        frame = new JFrame("Minesweeper");
        createMenuBar(frame);
        frame.setTitle("Nicholas Minesweeper");
        JPanel m = new JPanel(new GridLayout(9,9));
        for(int i = 0; i < GRID_WIDTH; i++) {
            for(int j = 0; j < GRID_HEIGHT; j++) {
                grids[i][j] = new JButton();
                grids[i][j].addActionListener(new ActionListener(){
                    public void actionPerformed(ActionEvent e){ 
                        if (squares[i][j] == 1)
                        {
                           System.out.println("BOmb");
                        }
                        else {
                            grids[i][j].setVisible(false);
                        }
                    }
                });
                m.add(grids[i][j]);
            }
        }
        frame.add(m, BorderLayout.CENTER);
        frame.setResizable(false);
        frame.setSize(width, heigth);

        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setVisible(true);
        frame.setSize(350, 250);
        frame.setVisible(true); 
    } 
}

2条回答
叼着烟拽天下
2楼-- · 2019-01-14 19:30

Anonymous inner classes have access to local variables through a trick behind the scenes. Local variable are implemented as hidden member variables of the inner class. They are assigned copies of the local variable. To prevent the copy value from being wrong, the Java compiler enforces that these local variables must be final so they aren't changed, so the copy stays correct.

The fields of the enclosing class don't need to be final; the local variables used must be final. You must make all local variables used in your anonymous inner class final. You can do this by declaring final variables to be initialized to your i and j values, and use them in your anonymous inner class.

// Inside the for loops in the createGrids method
grids[i][j] = new JButton();
// Declare x, y final
final int x = i;
final int y = j;
grids[i][j].addActionListener(new ActionListener(){
    public void actionPerformed(ActionEvent e){ 
        // Use x, y instead of i, j inside.
        if (squares[x][y] == 1)
        {
             System.out.println("BOmb");
        }
        else {
             grids[x][y].setVisible(false);
        }
    }
 });

Note that in Java 8, this would not be necessary, because the Java 8 compiler can detect if the local variables used in anonymous inner classes are "effectively final", that is, not final but never changed once initialized.

查看更多
走好不送
3楼-- · 2019-01-14 19:38

What is happening is that you are creating 81 ActionListener classes, each one with its own actionPerformed method. But when that method is executed, the class doesn't know what the value of i and j is anymore, since it left them far behind.

Java prevents this from happening, hence the compiler error. It requires any referenced local variables to be final so that it can pass them to the created classes.

The simplest way to solve this is to create a pair of final variables inside your loops:

for(int i = 0; i < GRID_WIDTH; i++) {
    for(int j = 0; j < GRID_HEIGHT; j++) {
        grids[i][j] = new JButton();

        final int x = i; // <-- Add these
        final int y = j;

        grids[i][j].addActionListener(new ActionListener(){
            public void actionPerformed(ActionEvent e){ 
                if (squares[x][y] == 1) // <-- change from squares[i][j]
                {
                   System.out.println("BOmb");
                }
                else {
                    grids[x][y].setVisible(false); // <-- change from grids[i][j]
                }
            }
        });
        m.add(grids[i][j]);
    }
}
查看更多
登录 后发表回答