Mutable variable capture in anonymous Java classes

The Java compiler requires local variables of enclosing contexts referenced in anonymous classes (so-called captured variables) to be final. What if the anonymous class wants to alter the value of the variable, i.e. requires the variable to be mutable? This post shows different ways how to achieve that.

The problem

Let’s assume we have a UI containing a button and a message to be displayed. The message is stored in a local variable. When clicking on the button, the value of the local variable should change:

   JButton button = new JButton("Press me!");
   String message = "Never been pressed";
   button.addActionListener(new ActionListener() {
 
      @Override
      public void actionPerformed(ActionEvent e) {
         message = "Pressed";
      }
 
   });

The Java compiler will complain with an error like this:

Cannot refer to a non-final variable message inside an inner class defined in a different method

The local variable message is captured by the actionPerformed method of the inner class, i.e. it is passed implicitly to the ActionListener by value. In order to avoid inconsistencies between the values of the “outer” variable and the “inner” variable, the Java compiler requires all captured variables to be final. But if the message is made final, its value cannot be changed any more by the ActionListener.

Mutability with an array

The most common way to solve this problem is to put the variable into an array of length one, make the array final and capture the array:

   JButton button = new JButton("Press me!");
   final String[] message = new String[]{"Never been pressed"};
   button.addActionListener(new ActionListener() {
 
      @Override
      public void actionPerformed(ActionEvent e) {
         message[0] = "Pressed";
      }
 
   });

The value is stored in the first slot of the array. The reference to the array is final but its element stays mutable.

While this approach works fine, it looks strange and will certainly raise questions for developers in your team which have never seen this construct before.

Mutability with a holder

The better way to make the message mutable is to put it into a holder class

    class Holder<T> {
        private T value;
 
        Holder(T value) {
            setValue(value);
        }
 
        T getValue() {
            return value;
        }
 
        void setValue(T value) {
            this.value = value;
        }
    }

and pass the holder into the inner class:

   JButton button = new JButton("Press me!");
   final Holder<String> mutableMessage = new Holder<String>("Never been pressed");
   button.addActionListener(new ActionListener() {
 
      @Override
      public void actionPerformed(ActionEvent e) {
         mutableMessage.setValue("Pressed");
      }
 
   });

This solution states more clearly why the message has been wrapped. If the holder is implemented as a generic utility class, this solution is not more verbose than the one with the array. In case you don’t want to implement the Holder class yourself, you can also reuse the MutableObject from Apache Commons or the Holder from Google Guava. One could argue that the solution with the array is faster (creating an array is usually faster than instantiating a class), but in most cases the performance loss will be negligible.

Lambda Expressions to the rescue?

Part of the upcoming JDK 8 is Project Lambda – one of its goals is to add support for Closures to the Java programming language. The so-called Lambda expressions offer a way to shorten the declaration of anonymous classes. Additionally I heard rumors that the Java 8 compiler does not require captured variables to be explicitly final, so I had a look at the JDK 8 early access release to see whether it can help me with the code above.

It turns out that Lambda Expressions help with shortening the code of the inner class:

   JButton button = new JButton("Press me!");
   final Holder<String> messageHolder = new Holder<String>("Never been pressed");
   button.addActionListener(e -> messageHolder.setValue("Pressed"));

However even with Lambda Expressions, it is not allowed to change the value of a captured variable. There is a new concept called effectively final which allows you to omit the final keyword – however the compiler still checks that the value of the variable is not changed in the Lambda Expression. Brian Goetz explains in his blog that allowing capture of mutable local variables in a multi-threaded environment would probably do more harm than good, therefore it will be prohibited. So the final keyword can only be omitted if the variable is not changed in the Lambda Expression body.

Conclusion

This post showed two ways to solve the problem of mutable captured variables in anonymous classes. The upcoming Lambda Expressions help to shorten the code of anonymous classes but do not change the requirements of the Java compiler regarding the mutability.

You may also like...

Share this Post

Twitter4
Google+4
LinkedIn
Facebook

Tags

2 Responses to “Mutable variable capture in anonymous Java classes”

  1. tb says:

    Encapsulation is ignored with theseapproaches. An inner anonymous class is still a different class and should try to avoid direct field access (at least if the fields are not public).
    The typical way to handle this (provide a settter) works perfectly in this case, too. Therefore, I would suggest something like this (lets hope formatting will work):

    @Override
    public void actionPerformed(ActionEvent e) {
    if (event is invalid)
    return;
    setMessage(“Pressed”);
    }

  2. @tb: Thanks for your feedback.

    One of the reasons for the existence of anonymous classes in the Java programming language is to keep the code concise, so proper encapsulation was not the main goal of the code discussed here. I do not agree that anonymous classes should avoid direct field access by any means. Anonymous classes are a concept which allows to define classes visible only in a very limited scope. In my opinion it is ok if they are tightly coupled with the class/method they are defined in.

    Your proposed solution improves encapsulation and avoids the problem with the mutable variable capture, but it has two consequences you should be aware of:
    * An additional method “setMessage” needs to be defined which is eventually only used by the anonymous class, therefore bloating the code.
    * Definition of this method is only possible if message is a field, not if it is a local variable of the method attaching the anonymous class.

2 responses so far

Written by . Published in Categories: EclipseSource News, Editors choice