从匿名内部类访问时,为什么要求局部变量为最终变量? [英] Why require local variables to be final when accessing from anonymous inner classes?

查看:199
本文介绍了从匿名内部类访问时,为什么要求局部变量为最终变量?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我们都知道您不能做这样的事情:

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

int a = 7;
new Runnable() {
     public void run() {
         System.out.println(a);
     }
}.run();
...

...没有将a定为最终值.我知道其中的技术原因,这是因为局部变量存在于堆栈中,除非您知道它不会更改,否则您将无法安全地进行复制.

...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.

然而,我很难理解的是,为什么编译器没有实现hack,所以当看到上述情况时,它会编译为类似以下内容:

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

然后,我们处于可以安全地从匿名内部类访问a的位置,并且可以根据需要更改它.当然,只有在我们实际更改a时,它才会这样做.据我所知,这将是一件相对简单的事情,适用于所有类型,并且允许从任何上下文中更改a.当然,可以将上述建议更改为对多个值使用合成包装类,或者使用另一种效率更高的方法,但是想法是相同的.我猜这对性能影响不大,但是我怀疑它是否过高,特别是在引擎盖下可能进行更多优化的情况下.除了可能依赖合成场的某些反射式调用以某种方式打破之外,我看不到许多缺点,但我从未听说过它是认真提出过的!有原因吗?

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?

推荐答案

在构造匿名内部类时,将复制其中使用的所有变量的 values .因此,如果内部类尝试更改变量的值,则不会可见.例如,假设这是有效的:

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

您可能期望将其打印为5(实际上在C#中会显示).但是,因为只采用了 copy ,它实际上会打印7 ...如果允许的话,不会有更大的变化.

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.

当然,对Java 可以进行了更改,以真正捕获变量而不是其值(因为C#用于匿名函数).这需要自动创建一个额外的类来存储本地"变量,并使方法和匿名内部类都共享该额外类的实例.那样会使匿名内部类更强大,但更难理解. C#决定采用复杂但功能强大的方法; Java采用了限制性但简单的方法.

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.)

请注意,至少使用C#规则,捕获变量"方法涉及相当大的复杂性.例如:

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

List<Runnable> runnables = new ArrayList<Runnable>();
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.

这篇关于从匿名内部类访问时,为什么要求局部变量为最终变量?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆