java 访问内部类的局部变量需要声明为final

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/27558425/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-11-02 11:59:33  来源:igfitidea点击:

Local variable access to inner class needs to be declared final

javascopeanonymous-class

提问by yoadle

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.

我遇到了对内部类的局部变量访问需要声明为final的问题。从方法 createGrids() -> " squares[i][j] = 0;" 可以看出 i 是一个需要声明为 final 的局部变量。我不知道为什么,我在字段中添加了 final 但它不起作用。

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

回答by rgettman

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 finalso they aren't changed, so the copy stays correct.

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

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 finalvariables to be initialized to your iand jvalues, and use them in your anonymous inner class.

封闭类的字段不需要是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);
        }
    }
 });

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 finalbut never changed once initialized.

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

回答by Michael Myers

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

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

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.

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

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

解决此问题的最简单方法是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]);
    }
}