更改 JButton ActionEvent 中的参数值 returns 错误,"must be final or effectively final"

Changing value of parameter within a JButton ActionEvent returns error, "must be final or effectively final"

我正在尝试创建一个创建 JButton 的函数。该按钮增加或减少 R、G 或 B 值(红色、绿色、蓝色)。用户输入几个参数,

所以如果用户希望增加绿色,整数值 userGreen 应该增加 15。但是当我尝试增加它时我得到一个错误,说:

Local variable userRed defined in an enclosing scope must be final or effectively final

我的实际程序附加了很多变量和其他函数,附加整个程序会太长,所以我尽可能地做了最短的例子。此示例简单地计算按钮被按下的次数并将其打印到控制台。

这会产生同样的错误

Local variable clickNumber defined in an enclosing scope must be final or effectively final

是否可以更改 JButton 的 Action Listener 中的参数值?为什么变量需要是最终的?

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

如果在局部 class 外部更改变量,则无法访问该变量。这就是 JLS(Java 11,第 8.1.3 段)所说的:

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


更新:如果你想要一个解决方法,你可以试试这个:

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;