Обработчик события имеет ошибку компиляции: «локальные переменные, на которые ссылается лямбда-выражение, должны быть окончательными» - PullRequest
1 голос
/ 03 апреля 2020

Я переместил весь свой код из ActionEvent в метод, но компилятор все еще выдает мне ту же ошибку: «локальные переменные, на которые ссылается лямбда-выражение, должны быть окончательными».

Это исходит от playerMove() позвоните сюда:

for (int row = 0; row < shooterBoard.length; row++) {
    for (int col = 0; col < shooterBoard[row].length; col++) {
        shooterBoard[row][col].setOnAction((ActionEvent e) -> {
            playerMove(memoryBoard, shooterBoard, playerText, winCounter, row, col);
        });
    }
}

Как мне решить эту проблему, и вы можете объяснить, почему я ее получаю? Я довольно новичок в кодировании и хочу лучше понять его.

Вот моя полная программа:

import javafx.application.Application;
import javafx.application.Platform;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.event.ActionEvent;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.TextArea;
import javafx.scene.control.TextField;
import javafx.scene.Scene;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.GridPane;
import javafx.scene.paint.Color;
import javafx.scene.text.Font;
import javafx.scene.text.FontWeight;
import javafx.stage.Stage;

import java.util.Scanner;
import java.io.File;
import java.io.FileNotFoundException;

public class BattleshipP2 extends Application{
        Image hit = new Image(getClass().getResourceAsStream("H"));
        Image miss = new Image(getClass().getResourceAsStream("M"));

    public static void grid(String[] args) {
        launch(args);
    }

    public void playerMove(char[][] memoryBoard, Button[][] shooterBoard, TextArea playerText, int winCounter, int row, int col) {


        if (memoryBoard[row][col] != 'X') {
                            if (memoryBoard[row][col] == '*') {
                                memoryBoard[row][col] = 'X';
                                shooterBoard[row][col].setGraphic(new ImageView(hit));
                                playerText.setText("You attacked " + row + " " + col + " and hit! Great job captain!");
                                winCounter++;
                            } else {
                                memoryBoard[row][col] = 'X';
                                shooterBoard[row][col].setGraphic(new ImageView(miss));
                                playerText.setText("You attacked " + row + " " + col + " and missed! Nice try captain!");
                            }
                        } else {
                            playerText.setText("Invalid Move!");
                        }
    }

    public static void loadFile(Button[][] board, char[][] memoryBoard, String fileName, TextArea text, boolean status) {
        try {
            Scanner file = new Scanner(new File(fileName));
            while (file.hasNextLine()) {
                for (int row = 0; row < board.length; row++) {
                    for (int col = 0; col < board[row].length; col++) {
                        if (status) {
                            board[row][col].setText(file.next());
                        } else {
                            memoryBoard[row][col] = file.next().charAt(0);
                            board[row][col].setText("*");
                        }   
                    }
                }
            }
        }
        catch (FileNotFoundException exception) {
            text.setText("Error opening file");
        }
    }

    public void start(Stage primaryStage) {
        GridPane gridPane = new GridPane();

        TextArea playerText = new TextArea("PLAYER.txt");
        TextArea cpuText = new TextArea("CPU.txt");
        Label promptP = new Label("Player Messages");
        Label promptC = new Label("CPU Messages");
        Label xAxis = new Label("");
        Label yAxis = new Label("");
        Label xAxis2 = new Label("");
        Label yAxis2 = new Label("");
        Button[][] shooterBoard = new Button[10][10];
        Button[][] playerBoard = new Button[10][10];
        char[][] memoryBoard = new char[10][10];
        int winCounter = 0;
        Scene scene = new Scene(gridPane, 440, 300);

        gridPane.add(xAxis, 1, 0, 10, 1);
        gridPane.add(yAxis, 0, 1, 1, 10);

        xAxis.setGraphic(new ImageView(new Image(getClass().getResourceAsStream("cols.png"))));
        yAxis.setGraphic(new ImageView(new Image(getClass().getResourceAsStream("rows.png"))));

        for (int row = 0; row < shooterBoard.length; row++) {
            for (int col = 0; col < shooterBoard[row].length; col++) {
                playerBoard[row][col] = new Button();
                playerBoard[row][col].setPrefWidth(50);
                playerBoard[row][col].setPrefHeight(50);
                gridPane.add(playerBoard[row][col], 1 + col, 1 + row, 1, 1);
            }
        }

        gridPane.add(xAxis2, 12, 0, 10, 1);
        gridPane.add(yAxis2, 11, 1, 1, 10);

        xAxis2.setGraphic(new ImageView(new Image(getClass().getResourceAsStream("cols.png"))));
        yAxis2.setGraphic(new ImageView(new Image(getClass().getResourceAsStream("rows.png"))));

        for (int row = 0; row < shooterBoard.length; row++) {
            for (int col = 0; col < shooterBoard[row].length; col++) {  
                shooterBoard[row][col] = new Button();
                shooterBoard[row][col].setPrefWidth(50);
                shooterBoard[row][col].setPrefHeight(50);
                gridPane.add(shooterBoard[row][col], 12 + col, 1 + row, 1, 1);
            }
        }

        promptP.setFont(Font.font("Boulder", FontWeight.BOLD, 14));
        promptC.setFont(Font.font("Boulder", FontWeight.BOLD, 14));
        playerText.setFont(Font.font("Boulder", FontWeight.BOLD, 22));
        cpuText.setFont(Font.font("Boulder", FontWeight.BOLD, 22));
        promptP.setTextFill(Color.web("#0000ff", 0.8));
        promptC.setTextFill(Color.web("#FF0000" , 0.8));
        playerText.setStyle("-fx-text-inner-color: blue;");
        cpuText.setStyle("-fx-text-inner-color: red;");

        gridPane.add(promptP, 0, 11, 3, 1);
        gridPane.add(playerText, 0, 12, 22, 1);
        gridPane.add(promptC, 0, 13, 3, 1);
        gridPane.add(cpuText, 0, 14, 22, 1);

        primaryStage.setTitle("Battleship GUI");  
        primaryStage.setScene(scene);
        primaryStage.show();

        loadFile(playerBoard, memoryBoard, playerText.getText(), playerText, true);
        loadFile(shooterBoard, memoryBoard, cpuText.getText(), cpuText, false);

        while (winCounter < 17) {
            for (int row = 0; row < shooterBoard.length; row++) {
                for (int col = 0; col < shooterBoard[row].length; col++) {
                    shooterBoard[row][col].setOnAction((ActionEvent e) -> {
                        playerMove(memoryBoard, shooterBoard, playerText, winCounter, row, col);
                    });
                }
            }
        }
    }
}

1 Ответ

0 голосов
/ 03 апреля 2020

переменная в лямбда-выражении должна быть конечной, так как в противном случае это приведет к непредвиденному поведению.

Лямбда-выражение - это сокращение от "реализации анонимного класса с одним методом" - для упрощения его

В рамках нового класса все может случиться с переменной, и внешняя сторона не увидит ее и не окажет никакого влияния. (передача по ссылке или передача по значению)

У вас есть 2 варианта. Использование (эффективного) параметра Final

// declaring them like this, makes them effectivly final because they aren't be assigned again, but instead declared and instanciated every time wich makes them "effectivley final" 
// the underscore is just for the naming and has no meaning otherwise. Its just, a exact copy of each Value
// but will be "effectivly final" 

int row_ = row;
int col_ = col;
String playerText_ = playerText;
... // and so on 
shooterBoard[row][col].setOnAction((ActionEvent e) -> {
                        playerMove(memoryBoard_, shooterBoard_, playerText_, winCounter_, row_, col_);
                    });

Использование объекта в качестве обертки, что по сути является окончательным.

В javaFX есть такая вещь, как Properties, которая очень удобна для подобных вещей.

Вместо примитивных типов используйте «упаковщик свойств», который будет создан только ОДИН РАЗ и будет эффективно окончательным, а затем каждая область сможет получить доступ к изменению.

Свойства также очень удобны для других вещей в javaFx. Как обратные вызовы при изменении.

Пример для Playertext

StringProperty playerText = new SimpleStringProperty().

playerText.set("bla bla")
System.out.println(playerText.get());

Это должно быть сделано для каждого параметра.

ИЛИ вы можете написать свою собственную обертку для объектов для всех Патраметров.

Поскольку вам нужны: memoryBoard, shooterBoard, playerText, winCounter, row, col

, вы можете создать объект, который является (эффективным) окончательным, и вместо него передать его.


public class GameInstanceModel{

   String playerText;
   int winCounter;
   int row;
   int col;
   //... your other Variables.

}

// on lunch 



public void start(Stage primaryStage) {
        GridPane gridPane = new GridPane();
        GameInstanceModel currentGame = new GameInstanceModel();
        // ...

        shooterBoard[row][col].setOnAction((ActionEvent e) -> {
                        playerMove(currentGame);
                    });

}

А затем создайте

...