【问题标题】:"Cannot refer to a non-final variable" from action handler?动作处理程序中的“不能引用非最终变量”?
【发布时间】:2012-01-14 10:46:21
【问题描述】:

我保证我已经阅读了无数关于此的帖子,所以我猜我无法将我读到的内容翻译成我想要做的事情。先说叙述。在基础上,我想做的是在我的示例中开发一个对象列表,这些对象是汽车。汽车列表显示在我的主窗口中。当我想添加一辆新车时,我打开一个对话框来设置属性并创建它。

问题是我一直在使用对象引用循环运行。我尝试使用公共方法创建子类并在构造函数中传递对象。这些尝试会导致关于非静态字段或非最终变量的错误。如果我能看到一个可靠的例子来说明如何进行引用,我认为其余的都会到位。

对象

public class Car {
private String size;

public Car(String sizeIn){
    this.size = sizeIn;
}   
public void setSize(String sizeIn){
    this.size = sizeIn;
}
public String getSize(){
    return this.size;
}
public String toString(){
    return this.size;
}
}

主窗口

import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.*;

import javax.swing.DefaultListModel;
import javax.swing.JFrame;
import javax.swing.JButton;
import javax.swing.JList;

public class MainGUI {

private JFrame frame;
private LinkedList<Car> carList;
private DefaultListModel model;

/**
 * Launch the application.
 */
public static void main(String[] args) {
    EventQueue.invokeLater(new Runnable() {
        public void run() {
            try {
                MainGUI window = new MainGUI();
                window.frame.setVisible(true);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    });
}

/**
 * Create the application.
 */
public MainGUI() {
    initialize();
}

/**
 * Initialize the contents of the frame.
 */
private void initialize() {
    frame = new JFrame();
    frame.setBounds(100, 100, 450, 300);
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

    JButton btnAddCar = new JButton("Add Car");
    btnAddCar.addActionListener(new EditLauncher());
    frame.getContentPane().add(btnAddCar, BorderLayout.NORTH);

    JList list = new JList(model);
    frame.getContentPane().add(list, BorderLayout.CENTER);
}

public void addCar(String size){
    Car car = new Car(size);
    carList.add(car);
    model.addElement(car);
    frame.getContentPane().invalidate();
    frame.getContentPane().validate();
}

public class EditLauncher implements ActionListener {

    @Override
    public void actionPerformed(ActionEvent e) {
        // TODO Auto-generated method stub

    }

}

}

对话框

import java.awt.BorderLayout;
import java.awt.FlowLayout;

import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JPanel;
import javax.swing.border.EmptyBorder;
import javax.swing.JTextField;
import javax.swing.JLabel;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;


public class EditDialog extends JDialog {

private final JPanel contentPanel = new JPanel();
private JTextField fldSize;
private MainGUI mainGUI;

/**
 * Launch the application.
 */
public static void main(String[] args) {

    try {
        EditDialog dialog = new EditDialog(mainGUI); //<<ERROR - Cannot make a static reference to the non-static field mainGU
        dialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
        dialog.setVisible(true);
    } catch (Exception e) {
        e.printStackTrace();
    }
}

/**
 * Create the dialog.
 */
public EditDialog(MainGUI mainGUI) {
    this.mainGUI = mainGUI;
    setBounds(100, 100, 225, 125);
    getContentPane().setLayout(new BorderLayout());
    contentPanel.setLayout(new FlowLayout());
    contentPanel.setBorder(new EmptyBorder(5, 5, 5, 5));
    getContentPane().add(contentPanel, BorderLayout.CENTER);
    JLabel lblSize = new JLabel("Size");
    contentPanel.add(lblSize);
    fldSize = new JTextField();
    contentPanel.add(fldSize);
    fldSize.setColumns(10);

    JPanel buttonPane = new JPanel();
    buttonPane.setLayout(new FlowLayout(FlowLayout.RIGHT));
    getContentPane().add(buttonPane, BorderLayout.SOUTH);
    JButton okButton = new JButton("OK");
    okButton.addActionListener(new ActionListener() {
        public void actionPerformed(ActionEvent e) {
            mainGUI.addCar(fldSize.getText()); // << ERROR Cannot refer to a non-final variable mainGUI inside an inner class defined in a different method
        }
    });
    okButton.setActionCommand("OK");
    buttonPane.add(okButton);
    getRootPane().setDefaultButton(okButton);

    JButton cancelButton = new JButton("Cancel");
    cancelButton.setActionCommand("Cancel");
    buttonPane.add(cancelButton);

}

}

【问题讨论】:

  • 那么,到底是什么问题?哪些代码行没有按您认为的那样工作?不,请不要说整个 50 多行都是“问题”:)
  • 快速通读……我也看不出在哪里构造了任何 carList? (可能应该在拥有引用的 MainGUI glass 的构造函数中)

标签: java oop object reference jframe


【解决方案1】:

对于“无法对非静态字段 mainGUI 进行静态引用”

问题是(EditDialogs)main 是静态的,因此只能访问静态成员或显式对象实例的成员(例如object.member 表单)。在这种情况下,mainGUI 不是静态成员,因此无法访问:这很好,因为如果调用“main”,则不会创建 MainGUI 实例! (请记住,每次启动应用程序时只使用一个main!去掉 EditDialog 中的“main”以避免这种混淆。)

相反,请考虑这一点(但请参阅 Dave 的评论以获得更好的方法!):

btnAddCar.addActionListener(new ActionListener () {
    public void actionPerformed(ActionEvent e) {
        EditDialog dialog = new EditDialog(MainGUI.this);
        dialog.show(); // or whatever
    }
});

对于“不能在不同方法中定义的内部类中引用非最终变量 mainGUI”

错误是因为只能在匿名内部类型 (new ActionListener() { ... }) 中使用成员变量(封闭类型)或“最终”局部变量。

现在,你可能会说“但是 mainGUI 是一个成员变量!”

嗯,是的,但是它被同名的局部变量遮蔽this.mainGUI vs mainGUI)。以下是我知道的解决方案:

  1. 更改局部变量名称以避免阴影:public EditDialog(MainGUI theMainGUI) ...

  2. 限定mainGUI 告诉Java 需要成员变量,从而绕过阴影:EditDialog.this.mainGUI.addCar(...),其中EditDialog 是封闭类型的名称。 (上面对MainGUI.this 采用了同样的方法。)

  3. final 注释mainGUI 参数public EditDialog(final MainGUI mainGUI) ...。成员变量仍将被遮蔽,但局部变量将满足访问要求,因为它是“最终的”。

编码愉快。

【讨论】:

  • 另一个建议——分离你的模型和视图。通过从对话框中调用 GUI 类上的addCar(),您正在通过视图修改模型。最好直接修改模型。查看模型-视图-控制器设计模式。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-03-28
  • 1970-01-01
  • 2019-08-19
  • 1970-01-01
  • 2010-11-20
相关资源
最近更新 更多