当前位置: 首页 > 知识库问答 >
问题:

事件处理程序有编译错误:“从lambda表达式引用的局部变量必须是最终变量”

姜胤
2023-03-14

我已经将所有代码从ActionEvent移到了一个方法中,但编译器仍然给我相同的错误:“从lambda表达式引用的局部变量必须是final”。

它来自此处的playerMobile()调用:

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个答案

刘运浩
2023-03-14

lambda表达式中的变量必须是final,否则会产生意外行为。

lambda表达式是“匿名类实现,只有一个html" target="_blank">方法”的简写——过于简化

在新类的范围内,任何事情都可能发生在一个变量上,外部不会看到它,也不会有任何影响。(引用传递vs值传递)

您有 2 个选项。使用(有效)最终参数

// 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中,你有一个叫做属性的东西,对于这样的事情来说真的很方便。

不使用基元类型,而是使用“属性包装器”,该属性包装器将仅实例化一次,并且将有效地最终化,并且每个作用域都能够访问更改。

这些属性对于javaFx中的其他东西也非常方便。比如更改时的回调。

Playertext的示例

StringProperty playerText = new SimpleStringProperty().

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

应该对每个参数都这样做。

或者可以为所有Patrametes编写自己的对象包装器。

因为你需要:记忆板,射击板,playerText,winCounter,行,列

您可以创建一个(有效地)最终的对象,然后传递它。


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

}

然后创建

 类似资料:
  • 我正在尝试从pojo创建一个函数,该函数在以下意义上使用细节类值的求和: 但是不知道为什么这一行< code > sum = sum . add(detail . getvalue());引发此错误: 从lambda表达式引用的局部变量必须是final或有效final 你能告诉我我做错了什么吗?谢了。

  • 我试图在中的forloop中更新,因为导致一个新的ComppletableFuture实例,我需要将其重新分配给一个新变量以更新,但我不太确定如何

  • 我有一个JavaFX 8程序(用于JavaFXPorts交叉平台),几乎可以做我想做的事情,但又短了一步。该程序读取文本文件,对行进行计数以建立随机范围,从该范围中选取一个随机数,然后读取该行以进行显示。 我对java有点陌生,但似乎我是否使用Lambda在中显示下一个随机行,我的

  • 问题内容: 我有一个JavaFX 8程序(用于跨平台的JavaFXPorts),可以完成我想做的事情,但只差了一步。该程序读取一个文本文件,对行进行计数以建立一个随机范围,从该范围中选择一个随机数,然后读取该行以进行显示。 我对Java有点陌生,但是似乎我是否使用Lambda都不显示下一个随机行,我的行期望一个静态值。 有什么想法可以调整我必须在每次按下屏幕按钮时简单显示var readln2的下

  • 这个程序是我的类的最终赋值,我在弄清楚为什么我收到错误“从内部类引用的局部变量必须是最终的或实际上是最终的”时遇到了问题。该程序正在运行并发线程来对#的数组进行排序,然后找到该数组的高值和低值。当我在没有并发的情况下创建它时,我没有这个错误。我正在努力确定在哪里最终确定高变量和低变量。 这是产生错误的代码块。如果我使int高=数字[0];或int-low=数字[0];final,然后我得到一个错误

  • 今天我想做一些我想完成的项目,在那里我得到了一个异常,我不能从lambda表达式中引用局部变量。我有一个方法,其中我给出了两个值,该方法检查值对是否已经在HashMap中 当它结束时,我想读出布尔函数,需要知道他是否发现它成立= false我怎样才能在这个lambda中设置founded或者有没有其他方法可以做到这一点?