14

我有按钮单击侦听器,在onCreate()方法中我有一个局部变量,例如

 onCreate() {

 super.onCreate();

 int i = 10;

 Button button = (Button)findViewById(R.id.button);

 button.setOnClickListener(new View.OnClickListener() {

        @Override
        public void onClick(View v) {
            i++;
        }   
    });

为什么java要求让我最终?

4

5 回答 5

29

当 onCreate() 方法返回时,您的局部变量将从堆栈中清除,因此它们将不再存在。但是匿名类对象 new View.OnClickListener() 引用了这些变量。当然这是错误的行为,所以java不允许你这样做。

在它是最终的之后,它变成一个常数。所以它存储在堆中,可以安全地用于匿名类中。

于 2011-10-21T08:58:29.750 回答
13

您的匿名内部类通过获取局部变量的副本来引用其封闭范围 - 如果您想更改匿名内部类中 int 的值,您需要做一些hackery:

final int[] arr = new int[1];
arr[0] = 10;
Button button = (Button)findViewById(R.id.button);

button.setOnClickListener(new View.OnClickListener() {
  arr[0]++;
}
于 2011-10-21T07:56:40.443 回答
12

因为您在匿名内部类中访问它。你不能那样做。您可以将其设为final,然后从匿名内部类中读取它,但是您不能增加它。

选项:

  • 使其成为外部类的实例变量
  • 改为使其成为匿名内部类的实例变量
  • 使用包装器 - 例如单元素数组,AtomicInteger或类似的东西

除非我需要i从其他任何地方到达,否则我可能会赞成第二种选择。老实说,我认为第三种选择有点令人讨厌。

于 2011-10-21T07:57:48.283 回答
5

制作变量final是必要的,因为在底层,像这样的匿名内部类只是语法糖,可以编译为包含方法范围之外的嵌套类。

这意味着内部类无法访问在方法内部声明的任何变量,因此编译器采用了另一个技巧 - 并将值复制到您的类的隐藏构造函数中。为避免程序员混淆此副本未更新以匹配方法中变量的更改,它必须是最终的以确保没有此类更改。

由于您的目标是拥有一个递增的整数,并且只有引用必须是最终的(对象本身不必是不可变的),您可以声明 afinal AtomicInteger i然后从回调中根据需要递增它。

于 2011-10-21T07:57:56.767 回答
4

由于您需要增加i变量,因此您无法使其成为最终变量。您可以改为使其成为班级成员。

于 2011-10-21T08:00:27.837 回答