Java:在封闭范围内定义的局部变量mi必须是最终的或有效的最终

时间:2015-11-19 09:26:13

标签: java

我收到错误,就像在主题中一样,我恳请你如何修复它... ERROR在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;
}

6 个答案:

答案 0 :(得分:19)

错误表示您不能在内部类中使用局部变量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);
    }
}

答案 1 :(得分:6)

是的,这是因为您正在从匿名内部类中访问mi变量,内部发生的是您的变量的另一个副本已创建并将在匿名内部类中使用,因此对于数据一致性,编译器会尝试限制您更改mi的值,这就是为什么它会告诉您将其设置为final。

答案 2 :(得分:4)

这里有一个非局部变量https://en.wikipedia.org/wiki/Non-local_variable),即您在一个匿名类的方法中访问局部变量。

方法的局部变量保留在堆栈上并在方法结束后立即丢失,但是即使在方法结束后,本地内部类对象仍然在堆上存活并且需要访问此变量(这里,当执行某个动作时)。

我建议两个解决方法: 您可以创建自己的实现actionlistenner的类,并将其作为构造函数参数,变量并将其保留为类属性。因此,您只能在同一对象中访问此变量。

或者(这可能是最好的解决方案)只是限定变量final的副本以在内部范围内访问它,因为错误建议使其成为常量:

这适合您的情况,因为您没有修改变量的值。

答案 3 :(得分:0)

正如我所看到的,数组仅是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);
}

}

答案 4 :(得分:0)

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

输出

i的值为8

如您所见,它现在可以正常工作,并且“在封闭范围内定义的局部变量必须是最终的或实际上是最终的”错误。

答案 5 :(得分:0)

一种解决方案是创建一个命名类而不是使用匿名类。给该命名的类一个构造函数,该构造函数可以采用您想要的任何参数并将它们分配给类字段:

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

现在,您可以照常创建此类的实例:

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