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);
}
}
You can declare the variable final, or make it an instance (or global) variable. If you declare it final, you won't be able to change it later. Any variable defined in a method and accessed by an anonymous inner class must be final.
A local inner class cannot be instantiated from outside the block where it is created in. Till JDK 7, the Local inner class can access only the final local variable of the enclosing block. However, From JDK 8, it is possible to access the non-final local variable of enclosing block in the local inner class.
Yes, we can access the local final variables using the method local inner class because the final variables are stored on the heap and live as long as the method local inner class object may live.
It can access any private instance variable of the outer class.
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.
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]);
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With