Why it says that “Cannot refer to a non-final variable i inside an inner class defined in a different method”?
我有button click listener,在onCreate()方法中,我有一个局部变量,比如
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| 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要求我做最后的决定?
当onCreate()方法返回时,本地变量将从堆栈中清除,因此它们将不再存在。但是匿名类对象new view.onclickListener()引用了这些变量。因为它是错误的行为,所以Java不允许你这样做。
最后,它变成一个常数。所以它存储在堆中,可以安全地在匿名类中使用。
- 好的。你提到我是本地人的原因是,OnCreate()完成后它会被销毁,所以OnClickListener()将无法获取i的值。但是如果我是最终的,那么我认为OnCreate()销毁后它也会被销毁。那么,即使它是常数,它将如何在onclicklistener中使用呢?
- 编译器只会在编译时用常量的值替换匿名类中的i,而您就不会遇到访问不存在的变量的问题。
- 对于最终变量,它是一种特殊情况吗?
- 不,OFC。你应该知道Java中的变量只是对对象的引用。如果变量不是最终变量,则其引用可能设置为空。但当它是最终的,你没有能力改变它。所以这个变量在方法返回后仍然存在。
匿名内部类通过复制局部变量来引用其封闭范围-如果要更改匿名内部类中int的值,则需要进行一些黑客操作:
1 2 3 4 5 6 7
| final int[] arr = new int[1];
arr [0] = 10;
Button button = (Button)findViewById (R. id. button);
button. setOnClickListener(new View. OnClickListener() {
arr [0]++;
} |
- 我喜欢这种方法…也许不清楚,但"黑客"。
- 另一个黑客:class Temp { public int i = 10; } final Temp temp = new Temp(),然后使用temp.i。
- 巧妙的!谢谢!
- 我更喜欢org.apache.commons.lang3.mutable.MutableIntfinal MutableInt = new MutableInt(10)。
因为你在一个匿名的内部类中访问它。你不能那样做。您可以使它成为最终版本,然后从匿名的内部类中读取它,但是您不能增加它。
选项:
- 将其改为外部类的实例变量
- 将其改为匿名内部类的实例变量
- 使用包装器-例如单个元素数组、AtomicInteger或类似的东西
我可能会赞成第二种选择,除非我需要从其他任何地方去i。老实说,我认为第三种选择有点讨厌。
- 当别人只给出理由时,只有你给出了解决办法(提出问题的人可能不期望这样想)。因此投票
制作变量final是必要的,因为在hood下,类似这样的匿名内部类只是语法上的糖分,编译为包含方法范围之外的嵌套类。
这意味着方法内部声明的所有变量都不能被内部类访问,因此编译器会采用另一种技巧——在类的隐藏构造函数中复制值。为了避免程序员混淆,如果不更新此副本以匹配方法中变量的更改,则必须最终确定是否存在此类更改。
因为这里的目标是要有一个递增的整数,并且只有引用必须是最终的(对象本身不必是不可变的),所以您可以声明一个final AtomicInteger i,然后根据自己的意愿从回调中递增。
因为您需要增加i变量,所以不能使其成为最终变量。你可以改为成为一个类成员。