关于java:“不能引用在不同方法中定义的内部类中的非final变量”问题

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



    }  
}


您可以声明一个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();
     }

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


这是正确的。它是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",则该特定算法将是错误的。为什么?因为到线程实际启动时,外循环很可能已经完成,线程都将看到值为Ni。这不是你想在这里实现的目标。

从Java 8开始更新,外部实例变量只需要有效地完成。


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