3

Is there a way to overcome the fact that variables accessed in an Anonymous Inner Class have to be final?

For example I have a button that should report the size of the list anytime. In the while loop later on the list should get modified and every cycle list will be newly instantiated, which is not possible if list is 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
}

A way to do this is making list a static variable, but thats not optimal. Is there another way to overcome this?

TomTom
  • 2,820
  • 4
  • 28
  • 46
  • The list doesn't have to be a static variable. It only needs to be a mutable field of an object that the button has access to. – JB Nizet Jan 17 '15 at 08:49
  • 1
    Do you actually need to reassign it or could you just clear it and update it? – khelwood Jan 17 '15 at 08:51
  • @khelwood No, not really. I just assume it would be faster to create a new instance. – TomTom Jan 17 '15 at 08:53
  • See https://commons.apache.org/proper/commons-lang/javadocs/api-3.1/org/apache/commons/lang3/mutable/MutableObject.html – Sergey Alaev Jan 17 '15 at 09:30

2 Answers2

3

No, there is not.

This is not a thing that can be overcome. As anonymous classes accessing field variables are closures, the scope of the field variable extends past the lifetime of the original declaration.

The way Java implements this is to create a local copy of the reference in the anonymous class. You can think of this of this as equivalent to:

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

So you can see that the reference to list in MyButtonImpl is a different reference. Reassigning the reference in the method will not affect the reference in MyButtonImpl.

For this reason Java does not allow non (effectively) final fields to be referenced in anonymous classes - it would not behave as expected.

As for dealing with you specific issue, either:

  • don't reassign list, just use clear(); or
  • create a local class, as I have done, and use its reference instead; or
  • use a mutable container, such as an AtomicReference

The last suggestion is a dirty hack, but can be useful in some, rare, cases.

Boris the Spider
  • 59,842
  • 6
  • 106
  • 166
2

One way to overcome this would be to make list an instance variable of the parent class. That way you will always have access to the variable in the anonymous class, and it should be possible to initialize it too.

Since its an inner class, it will have access to the instance variable of the parent class.

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

    }
ZakiMak
  • 2,072
  • 2
  • 17
  • 26