在匿名类中解决最终变量问题

3
有没有办法克服匿名内部类中访问变量必须是final的缺点?
例如,我有一个按钮,应该随时报告列表的大小。在稍后的while循环中,列表应该被修改,并且每个周期列表都将被新实例化,如果列表是final,则不可能完成这一点。
// 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 变成静态变量,但这并不是最优的解决方案。是否有其他方法来解决这个问题?

列表不必是一个静态变量。它只需要是按钮可以访问的对象的可变字段即可。 - JB Nizet
1
你实际上需要重新分配它吗,还是只需清除并更新它? - khelwood
@khelwood 不是的,我只是认为创建一个新实例会更快。 - TomTom
请参见 https://commons.apache.org/proper/commons-lang/javadocs/api-3.1/org/apache/commons/lang3/mutable/MutableObject.html。 - Sergey Alaev
我会尝试那个选项!谢谢! - TomTom
2个回答

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

因此,您可以看到MyButtonImpl中对list的引用是一个不同的引用。 在方法中重新分配引用将不会影响MyButtonImpl中的引用。
由于这个原因,Java不允许在匿名类中引用非(有效地)final字段 - 它将不能按预期运行。
至于处理您的具体问题,要么:
- 不要重新分配list,只需使用clear(); 或者 - 创建一个本地的class,如我所做的那样,并使用它的引用; 或者 - 使用可变容器,比如AtomicReference 最后一个建议是一种肮脏的技巧,但在一些罕见的情况下可能很有用。

2

克服这个问题的一种方法是将列表作为父类的实例变量。这样,您将始终可以访问匿名类中的变量,并且应该可以进行初始化。

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

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

    }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接