I am trying to make a function that creates a JButton
. The button either increases or decreases an R, G, or B value(red, green, blue). The user enters a few parameters,
So if the user wishes to increase green, the integer value, userGreen
should increase by 15. However when I tried increasing it I got an error, saying:
Local variable userRed defined in an enclosing scope must be final or effectively final
My actual program has a lot of variables and other functions attached and attaching the whole program would be far too long, so I made the shortest example I could. This example simply counts the number of times the button is pressed and prints it to the console.
This produces the same error
Local variable clickNumber defined in an enclosing scope must be final or effectively final
Is it possible to change the value of a parameter within a JButton's Action Listener? And why does the variable need to be final?
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class ButtonTest {
int clickNumber = 0;
public ButtonTest() {
JFrame frame = new JFrame("Button Test");
frame.add(newButton(frame, "Click me", clickNumber));
frame.pack();
frame.setVisible(true);
}
private JButton newButton(JFrame parent, String text, int clickNumber) {
JButton btn = new JButton(text);
btn.setBackground(Color.WHITE);
btn.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
System.out.println(clickNumber); //error here, "Local variable clickNumber defined in an enclosing scope must be final or effectively final"
}
});
clickNumber += 1; // if i remove this than the error goes away, so changing the value of the variable is somehow related
parent.add(btn);
return btn;
}
public static void main(String[] args) {
try {
String laf = UIManager.getCrossPlatformLookAndFeelClassName();
UIManager.setLookAndFeel(laf);
}
catch (Exception e) {}
SwingUtilities.invokeLater(new Runnable(){
public void run() {
new ButtonTest();
}
});
}
}
You cannot access a variable in a local class if you change it outside it. This is what JLS (Java 11, paragraph 8.1.3) says about it:
Any local variable, formal parameter, or exception parameter used but not declared in an inner class must either be declared final or be effectively final (§4.12.4), or a compile-time error occurs where the use is attempted.
Link: https://docs.oracle.com/javase/specs/jls/se11/html/jls-8.html#jls-8.1.3
UPD: If you want a workaround though, you can try this:
int finalWorkaround = clickNumber;
btn.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
// Our finalWorkaround is effectively final here, so we can use it
System.out.println(finalWorkaround);
}
});
clickNumber += 1;