Java: The local variable mi defined in the scope must be final or effectively finite

I get an error, as in the topic, and I ask you how to restore it ... THE ERROR is in menuItem-loop, where I try to set the foreground color of textArea to one selected 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; } 
+18
source share
5 answers

The error means you cannot use the local variable mi inside the inner class .


To use a variable inside an inner class, you must declare it final . While mi is a loop counter and final variables cannot be assigned, you must create a workaround to get the mi value in the final variable, which can be accessed in the inner class:

 final Integer innerMi = new Integer(mi); 

So your code will be like this:

 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])); // workaround: final Integer innerMi = new Integer(mi); Jmi.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { JMenuItem item = (JMenuItem) e.getSource(); IconA icon = (IconA) item.getIcon(); // HERE YOU USE THE FINAL innerMi variable and no errors!!! Color kolorIkony = getColour(colors[innerMi]); textArea.setForeground(kolorIkony); } }); mnForeground.add(Jmi); } } 
+26
source

Yes, this is because you are accessing the mi variable from your anonymous inner class, what happens deep inside is that another copy of your variable is created and will be used inside the anonymous inner class, therefore, for data consistency, the compiler will try to limit you by changing the value of mi , therefore, its instruction to indicate its final.

+7
source

Here you have a non-local variable ( https://en.wikipedia.org/wiki/Non-local_variable ), that is, you get access to the local variable in the method of the anonymous class.

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

I would suggest two workarounds: Either you create your own class that implements the actionlistenner and takes your variable as a constructor argument and saves it as an attribute of the class. Therefore, you should only access this variable inside the same object.

Or (and this is probably the best solution) just qualify a copy of the final variable to access it in the inner scope, since the error involves making it permanent:

This will suit your case since you are not changing the value of the variable.

+4
source

As I see it, the array consists only of String. For each loop, you can use to get a separate element of the array and put them in a local inner class for use.

Below is a code snippet for this:

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

}

0
source
 @FunctionalInterface interface IFunc{ void display(); } public class InnerDemo { public static void main(String[] args) { int i = 7; // lambda expression that implements the display method // of the IFunc functional interface IFunc ifunc = ()-> System.out.println("Value of i is " + i++); // Calling the display method ifunc.display(); } } Here I have changed the i to i++ in System.out thus the program will give compiler error "Local variable i defined in an enclosing scope must be final or effectively final". @FunctionalInterface interface IFunc{ void display(); } public class InnerDemo { public static void main(String[] args) { int[] numArr = {7}; // lambda expression that implements the display method // of the IFunc functional interface IFunc ifunc = ()-> System.out.println("Value of i is " + (numArr[0]+1)); // Calling the display method ifunc.display(); } } 

Output

Value i 8

As you can see, this now works, and "the local variable defined in the enclosing scope must be final or actually final", the error is no longer generated.

0
source

Source: https://habr.com/ru/post/1236309/


All Articles