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