"不能引用在不同方法中定义的内部类中的非final变量"问题

use*_*091 8 java

我不能在run()方法中使用变量"i".有什么办法吗?

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();
         } 



    }   
}
Run Code Online (Sandbox Code Playgroud)

Ale*_* C. 22

您可以声明一个final变量p,该变量将取i每次迭代的值.

    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();
     } 
Run Code Online (Sandbox Code Playgroud)

为什么我需要最后的声明?


Ste*_*n C 8

那就对了.这是Java内部类的限制!除非将该变量声明为,否则不允许内部类访问封闭范围中的实例变量final.

所以你需要像这样实现那些代码......

    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();
    }
Run Code Online (Sandbox Code Playgroud)

这种限制的原因是为了避免Java需要实现闭包.由于ii是最终的,编译器可以通过ii在实例化时将值的副本传递给匿名内部类来实现上述内容.它存储在隐藏变量中......以便在封闭方法调用完成后使用它.

如果没有这个限制,(假设的)Java编译器需要创建一个堆对象来保存i变量.(或者至少如果它无法优化对象的话......)这就是关闭 ......在实现层面.


人们有时会错过的另一点是,如果Java允许您访问非最终版本,则此特定算法将是不正确的i.为什么?因为在线程实际启动时,外部循环很可能已经完成,并且线程将全部看到i该值N.这不是你想要在这里实现的.


UPDATE从Java 8开始,外部实例变量只需要有效最终.