为什么变量必须在匿名方法中为final,而类字段不要[重复]

问题描述 投票:5回答:4

此问题已经在这里有了答案:

如果我有此匿名方法,则应将x变量声明为final。

private void testMethod (ListField<BeanModel> listField){

     final ListLoader<BeanModel> loader = new PagedListLoader<BeanModel>();
     listField.addListener(Events.Attach, new Listener<ListViewEvent<BeanModel>>() {

            @Override
            public void handleEvent(ListViewEvent<BeanModel> be) {
                loader.load();
            }
          });
}

但是,如果加载器是一个类字段,则不必将其声明为final:

public class testClass{

    private ListLoader<BeanModel> loader = new PagedListLoader<BeanModel>();

    private void testMethod (ListField<BeanModel> listField){
        listField.addListener(Events.Attach, new Listener<ListViewEvent<BeanModel>>() {

                    @Override
                    public void handleEvent(ListViewEvent<BeanModel> be) {
                        loader.load();
                    }
                  });

        //Could I modify loader's reference here, before the method executes?
        //loader = null;
        }
}

有人知道为什么保证局部变量在访问时不会改变,但不对类字段进行改变的原因吗?

java attributes anonymous-methods
4个回答
4
投票

局部变量在堆栈中分配,在testMethod()之后将超出范围。将变量设置为final可以确保将对它的引用传递给匿名类是可以的。如果不是最终值,则以后在testMethod()中对其进行赋值可能会稍后更改该值,从而导致结果混乱。 (用户可能期望使用后来分配的值,但这是不可能的。)>

但是,可以通过匿名类的父引用访问父类的字段,因此以后的任何分配都可以处理而不会引起混淆。


9
投票

根据java docs

匿名类可以访问其封闭类的成员。匿名类无法在其封闭范围内访问未声明为final或有效地不是final的局部变量(有效地final表示变量在初始化后从未更改。方法参数通常实际上是final。)

如果我们对局部类的实现方式有了一些了解,那么这种限制的原因就显而易见了。匿名本地类可以使用本地变量,因为编译器会自动为该类提供一个私有实例字段,以保存该类使用的每个本地变量的副本。编译器还将隐藏参数添加到每个构造函数中,以初始化这些自动创建的私有字段。因此,局部类实际上并不访问局部变量,而只是访问它们自己的私有副本。唯一可以正常工作的方法是将局部变量声明为final,这样可以保证它们不会更改。有了此保证,就可以确保局部类的变量的内部副本准确反映实际的局部变量。


1
投票

看看Java中的Lambda和Conjures。

匿名内部类没有周围的信息-您必须指定它是最终的,因此可以保证它的存在。

这可能与ListLoader的性质有关,但是我对使用此库没有经验。

希望我指出正确的方向。


1
投票

匿名类通过构造函数隐式获取局部变量。那就是他们得到他们使用的本地变量的副本。因此,如果我们在主代码中更改了变量值,则匿名类将不会看到此更改。声明local vars final有助于避免这种歧义。

© www.soinside.com 2019 - 2024. All rights reserved.