克服匿名类中的最终变量

时间:2015-01-17 08:45:18

标签: java anonymous-inner-class

有没有办法克服在匿名内部类中访问的变量必须是最终的这一事实?

例如,我有一个按钮,可以随时报告列表的大小。在稍后的list中,while循环应该被修改,并且每个循环list将被新实例化,如果list是最终的,则不可能这样做

// has to be final
final ArrayList<String> list = new ArrayList<String>();

MyButton button = new MyButton() {
    @Override
    public int getValue() {
        return list.size();
    }
};

while (true) {
    // modify/re-assign list
}

执行此操作的方法是使list成为静态变量,但这不是最佳的。还有另一种方法可以解决这个问题吗?

2 个答案:

答案 0 :(得分:3)

不,没有。

这不是一件可以克服的事情。由于访问字段变量的匿名类是闭包,因此字段变量的范围会超出原始声明的生命周期。

Java实现此目的的方法是在匿名类中创建引用的本地副本。您可以将此视为等同于:

final ArrayList<String> list = new ArrayList<String>();

class MyButtonImpl extends MyButton {
     final ArrayList<String> list;

     public MyButtonImpl(final ArrayList<String> list) {
         this.list = list;
     }

}

MyButton button = new MyButtonImpl(list);

因此,您可以看到list中对MyButtonImpl的引用是不同的引用。重新分配方法中的引用不会影响MyButtonImpl中的引用。

因此,Java不允许在匿名类中引用非(有效)final字段 - 它不会按预期运行。

至于处理具体问题,可以:

  • 不要重新分配list,只需使用clear();或
  • 像我一样创建一个本地class,并使用它的引用;或
  • 使用可变容器,例如AtomicReference

最后一个建议是肮脏的黑客攻击,但在一些罕见的情况下可能会有用。

答案 1 :(得分:1)

克服这种情况的一种方法是使 list 成为父类的实例变量。这样,您将始终可以访问匿名类中的变量,并且也可以初始化它。

由于它是一个内部类,因此它可以访问父类的实例变量。

    public class Parent{

        ArrayList<String> list = new ArrayList<String>();  

        // this inner class will work alongside the parent class
        // hence its possible to share the instance properties of Parent
        // with the Child.

        MyButton button = new MyButton() {
           @Override
           public int getValue() {
             return list.size();
           }
        };

    }