Loading [MathJax]/jax/output/CommonHTML/config.js
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >事件处理程序有编译错误:“从lambda表达式引用的局部变量必须是final”

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

Stack Overflow用户
提问于 2020-04-03 02:56:08
回答 1查看 131关注 0票数 1

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

它来自这里的playerMove()调用:

代码语言:javascript
运行
AI代码解释
复制
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);
        });
    }
}

我如何解决这个问题,你能解释为什么我会得到它吗?我是编程新手,想要更好地理解它。

下面是我的完整程序:

代码语言:javascript
运行
AI代码解释
复制
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);
                    });
                }
            }
        }
    }
}
EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2020-04-03 06:51:32

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

lambda表达式是“匿名类实现,只有一个方法”的简写--过度简化它

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

您有两个选项。使用(有效的)最后一个参数

代码语言:javascript
运行
AI代码解释
复制
// 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示例

代码语言:javascript
运行
AI代码解释
复制
StringProperty playerText = new SimpleStringProperty().

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

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

也可以为所有的Patrameters编写自己的对象包装器。

因为您需要: memoryBoard、shooterBoard、playerText、winCounter、row、col

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

代码语言:javascript
运行
AI代码解释
复制
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);
                    });

}

然后创建

票数 -1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/61004407

复制
相关文章

相似问题

添加站长 进交流群

领取专属 10元无门槛券

AI混元助手 在线答疑

扫码加入开发者社群
关注 腾讯云开发者公众号

洞察 腾讯核心技术

剖析业界实践案例

扫码关注腾讯云开发者公众号
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档