“Cannot refer to a non-final variable inside an inner class defined in a different method” issue
我不能在方法run()中使用变量"i"。有什么办法吗?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| public class Main {
public static void main (String[] args ) {
final int M = 100;
final int N = 4;
final int[] array = new int[M ];
for(int b = 0; b < M ; b ++) array [b ] = b ;
for( int i = 0; i < N ; i ++) {
new Thread(new Runnable() {
public void run () {
for(int a = i *(M /N );a < (i +1)*(M /N ); a ++)
System. out. println("Thread"+i +":"+array [a ]);
// i -> cannot refer to a non-final variable inside an inner class defined in a different method
}
}). start();
}
}
} |
- stackoverflow.com/questions/1299837/&hellip;
- 如果你用谷歌搜索你的错误,你将得到至少100个答案。
您可以声明一个final变量p,它将在每次迭代时取i的值。
1 2 3 4 5 6 7 8 9
| for( int i = 0; i < N ; i ++) {
final int p = i ;
new Thread(new Runnable() {
public void run () {
for(int a = p *(M /N );a < (p +1)*(M /N ); a ++)
System. out. println("Thread"+p +":"+array [a ]);
}
}). start();
} |
为什么我需要最后的声明?
- +1但请添加无法访问非最终变量的原因。
- 对。。。它奏效了!谢谢!
- 我有一个安静的常见问题:我想在另一个线程中启动一个线程,但我必须使该线程成为最终线程——这个解决方案对于我的特定情况是否也有效,或者我必须找到另一个"解决方法"?
这是正确的。它是Java内部类的限制!不允许内部类访问封闭范围中的实例变量,除非该变量声明为final。
所以你需要实现这样的代码…
1 2 3 4 5 6 7 8 9
| for (int i = 0; i < N ; i ++) {
final int ii = i ;
new Thread(new Runnable() {
public void run () {
for(int a = ii *(M /N );a < (ii +1)*(M /N ); a ++)
System. out. println("Thread"+ii +":"+array [a ]);
}
}). start();
} |
这种限制的原因是为了避免Java需要实现闭包。由于ii是最终的,编译器可以通过在实例化时将ii的值的副本传递给匿名内部类来实现上述功能。它存储在一个隐藏变量中…以便在封闭方法调用完成后使用它。
如果没有这个限制,一个(假设的)Java编译器就需要创建堆对象来保存EDCOX1×2变量。(或者至少如果不能把对象优化掉它会这样做…)这就是闭包的意义所在…在实施层面。
人们有时会错过的另一点是,如果Java允许您访问非最终EDCOX1的"2",则该特定算法将是错误的。为什么?因为到线程实际启动时,外循环很可能已经完成,线程都将看到值为N的i。这不是你想在这里实现的目标。
从Java 8开始更新,外部实例变量只需要有效地完成。
- 很好的解释!
- 即使引用变量也必须声明为final,但是引用变量只在运行时知道它们的实际对象引用,在这种情况下,编译器如何能够优化某些东西?
- 1)这不是优化问题。2)编译器实际做的是将最终/有效变量的值作为参数传递给匿名类构造函数。在运行时。
这就是Java内部类的局限性,因为Java不支持真正的闭包。
另外,检查它不能引用在不同方法中定义的内部类中的非最终变量以获取详细信息。
您可以这样尝试:
1 2 3 4 5 6 7 8 9
| for (int i = 0; i < N ; i ++) {
final int x = i ;
new Thread(new Runnable() {
public void run () {
for(int a = x *(M /N );a < (x +1)*(M /N ); a ++)
System. out. println("Thread"+x +":"+array [a ]);
}
}). start();
} |
尝试:
1 2 3 4 5 6 7 8 9
| for( int i = 0; i < N ; i ++) {
final currentIndex = i ; // declare final here
new Thread(new Runnable() {
public void run () {
for(int a = currentIndex *(M /N ) ; a < (currentIndex +1)*(M /N ) ; a ++)
System. out. println("Thread" + currentIndex +":" + array [a ]);
}
}). start();
} |