Java中无法给“final”变量赋值

13
 private void pushButtonActionPerformed(java.awt.event.ActionEvent evt)
{
    final int c=0;
    final JDialog d=new JDialog();
    JLabel l=new JLabel("Enter the Element :");
    JButton but1=new JButton("OK");
    JButton but2=new JButton("Cancel");
    final JTextField f=new JTextField(10);
    JPanel panel = new JPanel();
    but1.addActionListener(new ActionListener()
    {
        public void actionPerformed(ActionEvent e)
        {
            c=Integer.parseInt(f.getText());
            d.setVisible(false);
            d.dispose( );
        }
     });
but2.addActionListener(new ActionListener(){
    public void actionPerformed(ActionEvent e){
        d.setVisible(false);
        d.dispose( );
    }
});
}

我正在使用Netbeans 7.1.1。这是我的代码,在这里我将“c”声明为“final int”,但是当我输入“c=Integer.parseInt(f.getText());”时,我收到了一个错误“无法将值分配给final变量”。如果我从声明中删除单词"final",并将其作为“int c”进行定义,则在同一行上我会收到一个错误“无法从类内部访问局部变量c;需要声明为final”。有人能告诉我这是为什么吗?


6
无法在不同的类中定义的内部类中引用非最终变量 - Marko Topolnik
@MarkoTopolnik 不,这个标签是“swing” :P - lmat - Reinstate Monica
7个回答

35
你在一个函数中声明了 c , 然后创建了一个匿名内部类。这个内部类 ActionListener 持续存在,即使函数结束 - 所以它不能给 c 赋值,因为 c 是函数的局部变量。
警告信息“final”是有误导性的 - 这只是编译器告诉你无法从匿名类访问瞬态的局部变量。你不能仅仅通过将 c 设置为final来解决这个问题,因为那样会完全禁止对它的任何赋值,但你可以将 c 作为pushButtonActionPerformed所在的类的实例成员变量。示例如下:
class Something
{
    int c;

    private void pushButtonActionPerformed(java.awt.event.ActionEvent evt)
    {
        JButton but1=new JButton("OK");
        but1.addActionListener(new ActionListener()
        {
            public void actionPerformed(ActionEvent e)
            {
                c=Integer.parseInt(f.getText());
            }
        });
    }
}

1
“无法从匿名类访问瞬态局部变量”这种说法是错误的。同样错误的是,final 变量不能被赋值。如果这是真的,那么 final 变量就没有存在的意义了。实际上发生的是创建了一个闭包,它在匿名类声明的作用域内关闭了该变量。Java 只有一个限制,即只能关闭 final 变量。这是为了保持线程安全性。 - Marko Topolnik
你的代码示例是错误的,因为你没有创建闭包,所以匿名类的所有实例将共享相同的int。 - Marko Topolnik
除了Marko以上的不满,这是一个很好的解释并解决了我的问题。双手竖起大拇指。 - Ryan Knell

14

我将略过原因,直接给出解决方案:使用

final int[] c = {0};
...
c[0] = Integer.parseInt(f.getText());

详细信息请参考此帖子


2
我尝试了很多解决方案,只有这个诀窍有效。我的变量位于AsyncTask外部自定义类中。+1 - Jose Manuel Abarca Rodríguez
原因是,尽管使用final关键字强制c[]数组的地址为常量,但其内容并非如此:它们是可变的。我给你点赞。 - Gi0rgi0s

6
但是在这一行代码"c=Integer.parseInt(f.getText());"中,我遇到了一个错误:"无法为final变量赋值"。
是的。final变量的整个意义在于,您只能将其分配一次,但您正在尝试两次分配(一次在初始化时将其设置为0,一次在引用的代码行中)。根据规范
一个变量可以被声明为final。一个final变量只能被赋值一次。声明一个变量为final可以作为有用的文档,说明它的值不会改变,并有助于避免编程错误。如果在赋值之前没有明确未分配(§16),则将final变量分配给一个编译时错误。一个空白的final是一个声明缺乏初始化程序的final变量。final变量一旦被赋值,它总是包含相同的值。如果一个final变量持有一个对象的引用,那么对象的状态可能会因为对该对象的操作而改变,但变量始终指向同一个对象。

(强调是他们的,不是我的)

与其尝试在方法中跟踪您存储在变量中的状态,不如在您创建的匿名ActionListener实例的数据成员中跟踪它。


将您尝试跟踪的状态在C中变为您匿名ActionListener实例中跟踪的内容,而不是周围函数中的变量(我已更新答案以说明这一点)。 - T.J. Crowder

2

这就是为什么Java中存在关键字final的原因。变量是final的,即其值不能被更改。如果必须更改该值,请不要将变量标记为final。


3
由于 OP 在本地类中修改了变量,因此这并不能解决他的问题。 - Marko Topolnik

0

使用final关键字声明变量意味着其值不能被更改。final变量就像常量一样。


1
这不是 OP 的问题。他无法删除 final,因为他正在一个本地类中进行变异。 - Marko Topolnik

0

final 使“变量”成为常量——您无法更改它。至于为什么在删除 final 关键字后编译器会给出警告,我们需要看到整个代码...

编辑:

 but1.addActionListener(new ActionListener()
 {
     public void actionPerformed(ActionEvent e)
     {
         c=Integer.parseInt(f.getText());
         d.setVisible(false);
         d.dispose( );
     }
  });

我相信这段代码是导致编译器发出警告的原因。您不能在这个类中使用 c...


-4

只需从声明中删除final关键字,然后继续您的程序即可。因为final关键字表示值不可更改。


1
不是回答问题——OP已经说明了当他删除final时会得到另一个错误。 - Marko Topolnik

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接