Jump to content

Local variable defined in an enclosing scope

TheBean
Go to solution Solved by TheBean,

Thats actuallly a really good implementation. I didnt know evenhandlers had that .clickedButton function. 

But, I found a better implementation on stackoverflow. 

 

for (int i = 0; i < gridSize; i++) {
    for (int j = 0; j < gridSize; j++) {
        String text = i + "" + j;
        Button button = buttonGrid[i][j] ;
        button.setOnAction(e -> {
            button.setText(text);    
        });
        gridpane.getChildren().add(buttonGrid[i][j]);
    }
}

 

I have the following code for my minesweeper game. I am using JavaFX to create a 2D array of buttons to display. 

I have the error on the  " i " in the set on action line. It reads: Local variable i defined in an enclosing scope must be final or effectively final. I read it has something to do with using variables in the lambda expressions, but I didn't understand what they were saying. 

 

String text = "";
for (int i = 0; i < gridSize; i++) {
	for (int j = 0; j < gridSize; j++) {
		text += i + "" + j;
		buttonGrid[i][j].setOnAction(e -> {
			buttonGrid[i][j].setText(text);
		});
		
		gridpane.getChildren().add(buttonGrid[i][j]);
	}
}

 

Link to comment
Share on other sites

Link to post
Share on other sites

14 minutes ago, Saksham said:

Local variable i defined in an enclosing scope must be final or effectively final. I read it has something to do with using variables in the lambda expressions, but I didn't understand what they were saying. 

This line:

buttonGrid[i][j].setOnAction(e -> { buttonGrid[i][j].setText(text);});

 

Creates an Anonymous Lambda (<-- very thorough introduction to Lambdas in Java) with code:

buttonGrid[i][j].setText(text);

 

"Final" in Java speak is roughly equivalent to "constant", meaning that the value of the "thing" marked final must be fully computable at compile time, or only assigned to once at run time.

Unfortunately, I see no easy way to solve this. I am not familiar enough with Javas implementation of Lambdas to see a solution, I just hope that my previous link to Lambdas leads in the right direction.

ENCRYPTION IS NOT A CRIME

Link to comment
Share on other sites

Link to post
Share on other sites

You're effectively saying "when this button is clicked, run the following method". The problem is that the method references variables that haven't been defined in the method's scope.

 

This works if the variables are constants (or "final"), because then the compiler can just insert the literal values into the method that is generated.

 

Right now, your definition looks like this:

buttonGrid[i][j].setOnAction(e -> buttonGrid[i][j].setText(text));

The above code is equivalent to a method declared like this:

private void clickHandler() {
    buttonGrid[i][j].setText(text);
}

It should be easy to see that this isn't going to work, because the method refers to variables that haven't been defined (i, j, text). If the variables are constants the code would instead look like this:

private void clickHandler() {
    buttonGrid[1][2].setText("someText");
}

The proper solution would be to give all of those button the same click handler. The click handler then has to figure out which button was clicked based on the event ("e") it has received. I don't know JavaFX but I would expect the event to contain e.g. a reference to the button that was clicked. This should allow you to set that button's text. Something along the lines of:

buttonGrid[i][j].setOnAction(e -> e.clickedButton.setText(""));

 

Remember to either quote or @mention others, so they are notified of your reply

Link to comment
Share on other sites

Link to post
Share on other sites

Thats actuallly a really good implementation. I didnt know evenhandlers had that .clickedButton function. 

But, I found a better implementation on stackoverflow. 

 

for (int i = 0; i < gridSize; i++) {
    for (int j = 0; j < gridSize; j++) {
        String text = i + "" + j;
        Button button = buttonGrid[i][j] ;
        button.setOnAction(e -> {
            button.setText(text);    
        });
        gridpane.getChildren().add(buttonGrid[i][j]);
    }
}

 

Link to comment
Share on other sites

Link to post
Share on other sites

6 hours ago, Saksham said:

Thats actuallly a really good implementation. I didnt know evenhandlers had that .clickedButton function. 

Like I said, I haven't used JavaFX before 😅 I don't know whether it has such a property, I'm just saying I would expect it to have something similar to this. Sorry if I wasn't clear enough.

 

for (int i = 0; i < gridSize; i++) {
    for (int j = 0; j < gridSize; j++) {
        String text = i + "" + j;
        Button button = buttonGrid[i][j] ;
        button.setOnAction(e -> {
            button.setText(text);    
        });
        gridpane.getChildren().add(buttonGrid[i][j]);
    }
}

That implementation works, because the variables are now "effectively final". Personally, I'd declare them final just to make the intent more clear to your future self reading that code. The only difference would be in these two lines:

final String text = i + "" + j;
final Button button = buttonGrid[i][j];

It makes no functional difference, but it makes the intent more clear: These variables aren't modified in the code following their declaration.

Remember to either quote or @mention others, so they are notified of your reply

Link to comment
Share on other sites

Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×