对内部类的本地变量访问需要声明为final

时间:2014-12-19 01:13:06

标签: java scope anonymous-class

我遇到了一个局部变量访问内部类的问题,需要声明final。它来自方法createGrids() - > " squares[i][j] = 0;"我是一个需要被声明为final的局部变量。我不知道为什么,我已经在字段中添加了最终字段,但它的效果不佳。

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

//省略

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 个答案:

答案 0 :(得分:23)

匿名内部类可以通过幕后技巧访问局部变量。局部变量实现为内部类的隐藏成员变量。它们被分配了局部变量的副本。为了防止复制值出错,Java编译器强制要求这些局部变量必须为final,这样它们才不会被更改,因此副本保持正确。

封闭类的字段不需要final;使用的局部变量必须是final。您必须在匿名内部类final中使用所有局部变量。您可以将final变量声明为初始化为ij值,并在匿名内部类中使用它们。

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

请注意,在Java 8中,这不是必需的,因为Java 8编译器可以检测匿名内部类中使用的局部变量是否有效#&#34;,即不是{{1}但是一旦初始化就永远不会改变。

答案 1 :(得分:4)

发生的事情是您正在创建81个ActionListener类,每个类都有自己的actionPerformed方法。但是当执行该方法时,该类不再知道ij的值是什么,因为它远远落后于它。

Java防止这种情况发生,因此编译错误。它要求任何引用的局部变量都是最终的,以便它可以将它们传递给创建的类。

解决此问题的最简单方法是在循环中创建一对final变量:

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