Using final 1-element array for anonymous inner class


Answers

Using AtomicInteger or AtomicReference can make it little better. It is actually a common practice, but you can make it cleaner by introducing the actual class that implements ActionListener and provides the value through a getter.

Question

I stumbled across this trick for getting a value from an anonymous inner class to a variable which is declared in the outer class. It works, but it feels like a dirty hack:

private int showDialog()
{
    final int[] myValue = new int[1];

    JPanel panel = new JPanel();
    final JDialog dialog = new JDialog(mainWindow, "Hit the button", true);
    dialog.setDefaultCloseOperation( WindowConstants.DO_NOTHING_ON_CLOSE );

    JButton button = new JButton("Hit me!");
    button.addActionListener(new ActionListener()
    {
        @Override
        public void actionPerformed(ActionEvent e)
        {
            myValue[0] = 42;
            dialog.setVisible(false);
        }
    });

    panel.add(button);
    dialog.add(panel);
    dialog.pack();
    dialog.setVisible(true);

    return myValue[0];
}

(Yes, I realize this example could be replaced with a simple JOptionPane, but my actual dialogs are much more complicated.) The inner function insists that all variables it interacts with be final, but I can't declare the myValue as final because the inner function needs to assign it a value. Declaring it as a 1-element array gets around this problem, but seems like it might be a Bad ThingTM somehow. I'm wondering if a.) this is common practice or b.) there's any serious problems that could result from doing this.




A variable or parameter whose value is never changed after it is initialized is effectively final.

In case 1:

int[] idx don't change, if you replace idx[0]++; to idx = {1}; will compile error

In case 2:

if you remove idx++; it will compile fine




Java 8 Lambda variable scope

The root cause is that JVM lacks mechanisms of constructing references to local variables, which is what is needed to perform idx++ when idx is an int or some immutable type (e.g. String). Since you try to mutate idx, simply capturing its value would not be sufficient; Java would need to capture a reference, and then modify the value through it.

Java does not have this problem when you use an array, because arrays are reference objects. Java can capture array reference that never changes, and use that non-changing reference to mutate the object. Array itself provides the necessary level of indirection, because Java arrays are mutable.

I tried make idx static and move it outside the main method, working fine. But why?

Because in this situation there is no need for the lambda to capture a reference to a local variable of primitive type. A reference to the static variable is readily available, so there is no problem with capturing it.

Similarly, the code would work if you make idx a member variable, and use your lambda inside an instance method. This would let lambda modify idx field through this object, which could be freely captured.





Tags