2014-12-19 9 views
7

Ho un problema di accesso variabile locale alla classe interna che deve essere dichiarato definitivo. È dal metodo createGrids() -> "squares[i][j] = 0;" che i è una variabile locale che deve essere dichiarata definitiva. Non so perché e ho aggiunto il finale nei campi ma non funziona altrettanto bene.L'accesso locale variabile alla classe interna deve essere dichiarato finale

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

// omesso

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

Possibile duplicato di [La variabile è accessibile all'interno della classe interna. Deve essere dichiarato finale] (http://stackoverflow.com/questions/14425826/variable-is-accessed-within-inner-class-needs-to-be-declared-final) – Suragch

risposta

20

anonimi classi interne hanno accesso alle variabili locali attraverso un trucco dietro le quinte. Le variabili locali sono implementate come variabili membro nascoste della classe interna. Sono assegnate copie della variabile locale. Per impedire che il valore della copia sia errato, il compilatore Java impone che queste variabili locali siano final in modo che non vengano modificate, pertanto la copia rimane corretta.

I campi della classe di chiusura non devono essere final; le variabili locali utilizzate devono essere final. È necessario rendere tutte le variabili locali utilizzate nella classe interna anonima final. È possibile farlo dichiarando le variabili final da inizializzare ai valori i e j e utilizzarle nella classe interna anonima.

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

Si noti che in Java 8, questo non sarebbe necessario, perché il compilatore Java 8 in grado di rilevare se le variabili locali utilizzate nelle classi interne anonime sono "effettivamente finale", cioè non final ma mai cambiato una volta inizializzato.

+0

La vera domanda è perché usano una copia? – Ced

4

Quello che sta accadendo è che si sta creando 81 classi ActionListener, ognuna con un proprio actionPerformed metodo. Ma quando viene eseguito quel metodo, la classe non sa quale sia il valore di i e j, poiché li ha lasciati molto indietro.

Java impedisce che ciò accada, quindi l'errore del compilatore. Richiede che qualsiasi variabile locale di riferimento sia definitiva in modo che possa passarle alle classi create.

Il modo più semplice per risolvere questo è quello di creare un paio di final variabili all'interno del vostro loop:

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