【问题标题】:Local variable access to inner class needs to be declared final访问内部类的局部变量需要声明为final
【发布时间】:2015-02-17 22:32:55
【问题描述】:

我遇到了需要将内部类的局部变量访问声明为 final 的问题。来自方法 createGrids() -> "squares[i][j] = 0;" 的 i 是需要声明为 final 的局部变量。我不知道为什么,我已经在字段中添加了 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); 
    } 
}

【问题讨论】:

标签: java scope anonymous-class


【解决方案1】:

匿名内部类可以通过幕后的技巧访问局部变量。局部变量被实现为内部类的隐藏成员变量。它们被分配了局部变量的副本。为防止复制值出错,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 编译器可以检测匿名内部类中使用的局部变量是否“有效地最终”,即不是final,但一旦初始化就不会更改。

【讨论】:

  • 真正的问题是他们为什么要使用副本?
【解决方案2】:

您正在创建 81 个 ActionListener 类,每个类都有自己的 actionPerformed 方法。但是当执行该方法时,该类不再知道ij 的值是什么,因为它把它们远远抛在了后面。

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

解决此问题的最简单方法是在循环中创建一对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]);
    }
}

【讨论】:

    猜你喜欢
    • 2016-03-18
    • 1970-01-01
    • 2015-02-10
    • 1970-01-01
    • 2011-08-16
    • 1970-01-01
    • 2019-09-22
    • 2018-07-13
    • 2015-07-24
    相关资源
    最近更新 更多