[java] Java: Local variable mi defined in an enclosing scope must be final or effectively final

I get the error, as in subject, and I kindly ask you how to repair it... ERROR is in menuItem-loop, where I try to set the textArea foreground colour to one picked from menuItem: (colors[mi])

    String[] colors = {
            "blue", 
            "yellow",
            "orange",
            "red", 
            "white", 
            "black", 
            "green", 
            };

JMenu mnForeground = new JMenu("Foreground");
            for (int mi=0; mi<colors.length; mi++){
                String pos = Character.toUpperCase(colors[mi].charAt(0)) + colors[mi].substring(1);
                JMenuItem Jmi =new JMenuItem(pos);
                Jmi.setIcon(new IconA(colors[mi]));

                Jmi.addActionListener(new ActionListener() {
                    @Override
                    public void actionPerformed(ActionEvent e) {
                        JMenuItem item = (JMenuItem) e.getSource();
                        IconA icon = (IconA) item.getIcon();
                        Color kolorIkony = getColour(colors[mi]); // ERROR HERE: (colors[mi])
                        textArea.setForeground(kolorIkony);
                    }
                });

                mnForeground.add(Jmi);
            }

public Color getColour(String colour){
  try {
      kolor = Color.decode(colour);
  } catch (Exception e) {
      kolor = null; 
  }
  try {
        final Field f = Color.class.getField(colour);
        kolor = (Color) f.get(null);
      } catch (Exception ce) {
        kolor = Color.black;
      }
return kolor;
}

This question is related to java

The answer is


One solution is to create a named class instead of using an anonymous class. Give that named class a constructor that takes whatever parameters you wish and assigns them to class fields:

class MenuActionListener implements ActionListener {
    private Color kolorIkony;

    public MenuActionListener(Color kolorIkony) {
        this.kolorIkony = kolorIkony
    }

    @Override
    public void actionPerformed(ActionEvent e) {
        JMenuItem item = (JMenuItem) e.getSource();
        IconA icon = (IconA) item.getIcon();
        // Use the class field here
        textArea.setForeground(kolorIkony);
    }
});

Now you can create an instance of this class as usual:

Jmi.addActionListener(new MenuActionListener(getColor(colors[mi]));

As I can see the array is of String only.For each loop can be used to get individual element of the array and put them in local inner class for use.

Below is the code snippet for it :

     //WorkAround 
    for (String color : colors ){

String pos = Character.toUpperCase(color.charAt(0)) + color.substring(1);
JMenuItem Jmi =new JMenuItem(pos);
Jmi.setIcon(new IconA(color));

Jmi.addActionListener(new ActionListener() {
        @Override
        public void actionPerformed(ActionEvent e) {
            JMenuItem item = (JMenuItem) e.getSource();
            IconA icon = (IconA) item.getIcon();
            // HERE YOU USE THE String color variable and no errors!!!
            Color kolorIkony = getColour(color); 
            textArea.setForeground(kolorIkony);
        }
    });

    mnForeground.add(Jmi);
}

}


What you have here is a non-local variable (https://en.wikipedia.org/wiki/Non-local_variable), i.e. you access a local variable in a method an anonymous class.

Local variables of the method are kept on the stack and lost as soon as the method ends, however even after the method ends, the local inner class object is still alive on the heap and will need to access this variable (here, when an action is performed).

I would suggest two workarounds : Either you make your own class that implements actionlistenner and takes as constructor argument, your variable and keeps it as an class attribute. Therefore you would only access this variable within the same object.

Or (and this is probably the best solution) just qualify a copy of the variable final to access it in the inner scope as the error suggests to make it a constant:

This would suit your case since you are not modifying the value of the variable.


Yes this is happening because you are accessing mi variable from within your anonymous inner class, what happens deep inside is that another copy of your variable is created and will be use inside the anonymous inner class, so for data consistency the compiler will try restrict you from changing the value of mi so that's why its telling you to set it to final.