最终的本地变量无法赋值。

37

我有一个座位的数组,数组中有两个字符串(已选和空闲)。鼠标单击时,我想遍历数组并找到已选座位。但当我按下按钮时,它会显示:

最终的本地变量seatno无法分配, 因为它是在封闭类型中定义的。

    JButton btnContinue = new JButton("Next");
    btnContinue.addMouseListener(new MouseAdapter() {
        public void mouseClicked(MouseEvent arg0) {

            for(int x=0;x<17;x++){
                if(anArray[x]=="selected"){

                    seatno = anArray[x];
                }
            }

            data page=new data(newfrom,newto,newtime,date2,seatno);
            page.setVisible(true);
            setVisible(false);
        }
    });
    btnContinue.setBounds(358, 227, 62, 23);
    contentPane.add(btnContinue);

你的 seatno 变量中含有 final 关键字。 - Luiggi Mendoza
6个回答

97

重点是:封闭类型中的方法局部变量实际上被复制到匿名类的实例中(这是由于激活帧问题引起的,但我不会深入探讨,因为这与问题并不相关),这就是为什么它们需要是final的原因,因为嵌套类型实例中的变量不再是同一个变量了。

所以,这就是第一个例子:

void foo() {
    int a = 3;
    new Runnable() {
        @Override
        public void run() {
            a += 3;
        }
    };
}

这段代码无法通过编译,因为你不能在匿名类的方法中引用非 final 的变量。当你在 a 的声明中添加 final 修饰符时,a 的值将被复制到你定义的匿名类实例中。然而,你不能更改 a 的值,因为这些更改不会对声明 a 的方法可见。

然而,匿名类不是静态的,也就是说,它们具有对封闭实例的引用(除非它们所在的方法是静态的),你可以使用这个引用来修改封闭实例的变量:

int a = 3;

void foo() {
    new Runnable() {
        @Override
        public void run() {
            a += 3;
        }
    };
}

这个例子是可编译的,每次调用匿名类实例的run()方法时,a都会增加3。 (在这个例子中它从未被调用,但这只是一个例子。)

因此,总结一下,您需要将变量seatno从方法本地变量转换为封闭类型的实例变量。或者,如果已经是实例变量,则需要删除final修饰符,因为final变量只能赋值一次。

更新: 在Java 8中,引入了“有效final”变量的概念(请参见Java语言规范)。 但是,在本帖子的第一个示例中,变量a被多次赋值,这导致它不是“有效final”。 这意味着这个例子仍然无法在Java 8中编译。(编译错误为“定义在封闭范围内的局部变量a必须是final或有效final”)


4

一个final变量不能改变它的值(类似于C/C++中的const)。

你可能想把它作为类的字段(当然不带final关键字),而不是函数内的局部变量。


4

除了定义一个类成员变量之外,您还可以使用可变的int来实现相同的效果。

void foo() {
    final MutableInt a = new MutableInt(3);
    new Runnable() {
        @Override
        public void run() {
           a.add(3);
        }
    };
}

由于MutableInt不是原始类型(因此通过引用传递),并且可以重新分配,因此这个代码段可以正常工作。


3
请注意:MutableInt需要使用Apache Commons Lang库。 - Sundae
很好,谢谢您。MutableInt比AtomicInteger更方便,因为代码味道更少(需要更少的代码)。 - Hartmut Pfarr

2

我最近也遇到了类似的问题。在我的情况下,更容易创建最终数组(或集合),并将我想要在匿名类中更改的变量添加到此数组中,如下所示。

   int a = 3;
   final int[] array = new int[1];
   array[0] = a;
   new Runnable() {
       @Override
       public void run() {
           array[0] += 3;
       }
   };

0

不知道seatno的声明,我建议在mouseClicked()方法中引入一个新变量,它不是final,并且执行与当前seatno相同的工作,因为该变量似乎仅在该方法内部使用。

顺便说一下:将你的类名大写(data应该是Data)。看起来会更清晰。


0

确保你的变量没有使用 final 修饰符。

//final, can be set only when the object is created.
private final String seatno;

//no final modifier, the value can be set every time you "want"
private String seatno;

另外,要比较字符串应该使用equals方法:

if(anArray[x].equals("selected"))

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