闭包中的变量一般为局部变量,若是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";
}
}));
}
}