Java: Локальная переменная mi, определенная в охватывающей области, должна быть окончательной или эффективно конечной

Я получаю ошибку, как в теме, и я прошу вас, как ее восстановить... ОШИБКА находится в menuItem-loop, где я пытаюсь установить цвет переднего плана textArea в один выбранный из 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;
}

Ответ 1

Ошибка означает , вы не можете использовать локальную переменную mi внутри внутреннего класса.


Чтобы использовать переменную внутри внутреннего класса, вы должны объявить ее final. Пока mi является счетчиком цикла, а переменные final не могут быть назначены, вы должны создать обходное решение для получения значения mi в переменной final, доступ к которой можно получить во внутреннем классе:

final Integer innerMi = new Integer(mi);

Итак, ваш код будет таким:

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

Ответ 2

Да, это происходит потому, что вы получаете доступ к переменной mi из вашего анонимного внутреннего класса, что происходит глубоко внутри, так это то, что создается другая копия вашей переменной и будет использоваться внутри анонимного внутреннего класса, поэтому для согласованности данных компилятор попытается ограничить вас изменением значения mi, поэтому его указание указать его окончательный.

Ответ 3

Здесь у вас есть нелокальная переменная (https://en.wikipedia.org/wiki/Non-local_variable), то есть вы получаете доступ к локальному переменная в методе анонимного класса.

Локальные переменные метода хранятся в стеке и теряются, как только метод заканчивается, однако даже после завершения метода локальный объект внутреннего класса все еще жив в куче и ему нужно будет получить доступ к этой переменной (здесь, когда действие выполняется).

Я бы предложил два обходных пути: Либо вы создаете свой собственный класс, который реализует actionlistenner, и принимает в качестве аргумента конструктора вашу переменную и сохраняет ее как атрибут класса. Поэтому вы должны получить доступ только к этой переменной внутри одного и того же объекта.

Или (и это, вероятно, лучшее решение) просто квалифицируйте копию переменной final для доступа к ней во внутренней области, поскольку ошибка предполагает сделать ее постоянной:

Это подойдет вашему делу, так как вы не изменяете значение переменной.

Ответ 4

Как я вижу, массив состоит только из String. Для каждого цикла можно использовать, чтобы получить отдельный элемент массива и поместить их в локальный внутренний класс для использования.

Ниже приведен фрагмент кода для этого:

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

}

Ответ 5

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

Выход

Значение я 8

Как вы можете видеть, теперь это работает, и "локальная переменная, определенная во включающей области видимости, должна быть окончательной или фактически конечной", ошибка больше не генерируется.