Home java匿名内部类不能修改外部闭包参数
Post
Cancel

java匿名内部类不能修改外部闭包参数

闭包中的变量一般为局部变量,若是int这种基础变量类型的以及String这种特殊类型的变量。变量是创建在栈上的。当传入这种类型的参数时,相当于重新拷贝了一份对象的引用到内部类中。此时参数也并非和外部类相同的引用了,只是指向同一个地址而已,所以只能去修改指向对象的属性,而不能修改其指向。

1
2
3
4
5
6
7
8
9
10
11
12
13
public class UndefineTest {

    private String name = "demo";

    public void testFun(int x) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                name = "jiangker"; // OK
            }
        });
    }
}

从编译最后生成的文件来看,实则是把外部对象通过构造函数给传入到了内部对象中

1
2
3
4
5
6
7
8
9
class UndefineTest$1 implements Runnable {
    UndefineTest$1(UndefineTest this$0) {
        this.this$0 = this$0;
    }

    public void run() {
        UndefineTest.access$002(this.this$0, "jiangker");
    }
}

若是方法里的参数,则不能修改

1
2
3
4
5
6
7
8
9
10
11
public class UndefineTest {
    public void testFun(int x) {
         String name = "demo";
        new Thread(new Runnable() {
            @Override
            public void run() {
                name = "jiangker"; // not work
            }
        });
    }
}

实际传入是引用传递,指向相同的对象。需要为final,不能修改。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class UndefineTest {
    public void testFun(int x) {
        String name = "demo";
        new Thread(new Runnable() {
            @Override
            public void run() {
                String localName = name;
            }
        });
    }
}
// to
class UndefineTest$1 implements Runnable {
    UndefineTest$1(UndefineTest this$0, String var2) {
        this.this$0 = this$0;
        this.val$name = var2;
    }

    public void run() {
        String localName = this.val$name;
    }
}

但是在kotlin中,此类的写法又是OK的。

1
2
3
4
5
6
7
8
9
10
class UndefineKtTest {
    fun testFun() {
        var name = "demo"
        Thread(object : Runnable {
            override fun run() {
                name = "jiangker"
            }
        })
    }
}

反编译之后可以看到,kotlin实际是进行了一次语法糖上的封装。

1
2
3
4
5
6
7
8
9
10
11
public final class UndefineKtTest {
   public final void testFun() {
      final ObjectRef name = new ObjectRef();
      name.element = "demo";
      new Thread((Runnable)(new Runnable() {
         public void run() {
            name.element = "jiangker";
         }
      }));
   }
}

参考文档

This post is licensed under CC BY 4.0 by the author.