java匿名函数访问局部变量,从匿名内部类访问时,为什么要求局部变量为最终变量?...

We all know you can't do things like this:

int a = 7;

new Runnable() {

public void run() {

System.out.println(a);

}

}.run();

...

...without making a final. I get the technical reason why and it's because local variables live on the stack and you can't safely make a copy unless you know it won't change.

What I struggle to see however is why the compiler doesn't have an implementation hack so that it when it sees the above situation it compiles down to something like:

int[] a = {7};

new Runnable() {

public void run() {

System.out.println(a[0]);

}

}.run();

...

Then we're in the position where it's safe to access a from an anonymous inner class and indeed change it if we wish. Of course, it might only do this hack when we actually change a. As far as I could see this would be a relatively simple thing to put in, would work for all types and would allow a to be changed from whatever context. Of course, the above proposal could be changed to use synthetic wrapper classes for multiple values or another approach that's a bit more efficient, but the idea is the same. I guess there's a small performance hit, but I doubt it'd be excessive especially with the potential for more optimisations under the hood. Aside from perhaps certain reflective calls that rely on synthetic fields being a certain way breaking, I can't see many disadvantages, but I've never heard it seriously proposed! Is there a reason why?

解决方案

When the anonymous inner class is constructed, the values of all the variables used within it are copied. So if the inner class then tried to change the value of the variable, that wouldn't be visible. For example, suppose this were valid:

int a = 7;

Runnable r = new Runnable() {

public void run() {

a = 5;

}

};

r.run();

System.out.println(a);

You might expect it to print 5 (which indeed it would in C#). But because only a copy has been taken, it would actually print 7... if it were allowed, with no bigger changes.

Of course, Java could have been changed to really capture the variable instead of its value (as C# was for anonymous functions). That requires automatically creating an extra class to store the "local" variables, and make both the method and the anonymous inner class share an instance of that extra class. That would have made anonymous inner classes more powerful, but arguably harder to understand. C# decided to go for the power-but-complexity route; Java went for the restrictive-but-simple approach.

(Using an array instead of a custom class is valid for a single variable, but becomes more wasteful when there are multiple variables involved - you don't really want to have to create a wrapper object for every variable if you can help it.)

Note that there are significant complexities involved in the capture-the-variable approach, at least using the C# rules. For example:

List runnables = new ArrayList();

int outer = 0;

for (int i = 0; i < 10; i++) {

int inner = 0;

runnables.add(new Runnable() {

public void run() {

outer++;

inner++;

}

});

}

How many "inner" variables are created? One for each instance of the loop, or one overall? Basically, scopes make life tricky for this sort of thing. Feasible, but tricky.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值