Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/308.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
在为Java Swing元素实现ActionListener时,如何解决静态引用错误?_Java_Swing - Fatal编程技术网

在为Java Swing元素实现ActionListener时,如何解决静态引用错误?

在为Java Swing元素实现ActionListener时,如何解决静态引用错误?,java,swing,Java,Swing,我是一个相当新手的Java程序员,正在尝试使用JavaSwing元素创建一个简单的用户界面,该元素接受用户输入,并(最终)验证输入的日期的格式是否正确 我目前正在尝试使用一个主“createTextbox”类,该类生成表单,同时使用一个实现ActionListener的类来读取用户的输入,并在单击按钮时验证日期。我遇到的问题是,当我尝试在ActionListener类中从CreateTextbox引用我的字段时,我得到了一个错误,因为在静态上下文中引用我的字段是非静态的。但是,我不想将字段定义为

我是一个相当新手的Java程序员,正在尝试使用JavaSwing元素创建一个简单的用户界面,该元素接受用户输入,并(最终)验证输入的日期的格式是否正确

我目前正在尝试使用一个主“createTextbox”类,该类生成表单,同时使用一个实现ActionListener的类来读取用户的输入,并在单击按钮时验证日期。我遇到的问题是,当我尝试在ActionListener类中从CreateTextbox引用我的字段时,我得到了一个错误,因为在静态上下文中引用我的字段是非静态的。但是,我不想将字段定义为静态字段,因为我希望用户能够更改输入并再次单击按钮

以下是我的CreateTextbox类:

import javax.swing.JButton;
import javax.swing.JFrame;

import javax.swing.JLabel;
import javax.swing.JTextField;


public class CreateTextbox extends JFrame{

  JTextField stringEntry, dateEntry;
  JLabel stringEntryLabel, dateEntryLabel;
  JButton print;


  public CreateTextbox(){

    setLayout(null);
    stringEntry = new JTextField(1);
    stringEntry.setBounds(100,20,100,20);
    add(stringEntry);

    stringEntryLabel = new JLabel("String Name:");
    stringEntryLabel.setBounds(10,20,100,20);
    add(stringEntryLabel);

    dateEntry = new JTextField(1);
    dateEntry.setBounds(100,50,100,20);
    add(dateEntry);

    dateEntryLabel = new JLabel("Date:");
    dateEntryLabel.setBounds(10,50,100,20);

    print = new JButton("Validate");
    print.setBounds(20,260,100,20);
    print.addActionListener(new MyAction());
    add(print);

  }

  public static void main (String[] args)
  {
    CreateTextbox me = new CreateTextbox();
    me.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    me.setVisible(true);
    me.setLocation(550, 500);
    me.setSize(700, 700);
    me. setTitle("Create Textbox");

  }

} 
以及ActionListener类:

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.text.ParseException;

public class MyAction implements ActionListener {

  public void actionPerformed(ActionEvent e) {
    String strInput = CreateTextbox.stringEntry.getText();
    String strDate = CreateTextbox.dateEntry.getText();
    valiDate(strDate);
  }
  public void valiDate(String date) {
    //code to validate the date
  }
}

我猜这有一个我忽略的非常简单的解决方案。我已经看过几篇类似的文章,但我仍然很难找到解决方案。

我会将listener类内联。这样您就可以访问实例变量,即使它们是私有的

print.addActionListener(event -> {
  String strInput = CreateTextbox.stringEntry.getText();
  String strDate = CreateTextbox.dateEntry.getText();
  validate(strDate);
});

既然你们已经说你们正在学习,我将试着用“为什么和为什么不”来给你们一个教学上的答案,可能有点太长了,但希望它能帮助你们

当您引用
CreateTextbox.stringEntry
时,您引用的是一个类变量,这是一个静态变量,因为您实际上在语句的第一部分告诉我们的是一个类,而不是一个对象,这是该类的所有实例所共有的

另一方面,当您执行
this.stringEntry
时,您引用的是一个对象的变量,它不同于
this.stringEntry
,它们是其所有者对象的属性。必须指出的是,一般来说,让您的变量可见是一种不好的做法(当然有时不这样做是合理的),而是让其他人通过“getter”访问它们,这种方法允许您访问内部变量:
getStringEntry():JTextField
getDateEntry():JTextField
。但同样,您不想让其他人获得您的组件,因为当您只想公开其值时,可以从外部操纵它们,所以只需公开它们各自的文本:

public String getNameText() {
    return this.stringEntry.getText();
}

public String getDateText() {
    return this.dateEntry.getText();
}
然后,在另一个类中有一个
ActionListener
。当触发
ActionListener
的事件时,该方法将接收一个
ActionEvent
,其中包含对发起事件的对象的引用,并且可以通过方法
getSource()
进行访问。当您有一个外部类时,您希望使用该类“附加”到
CreateTextbox
,因此您希望在源中接收
CreateTextbox
,因此您可以访问如下实例:

@Override
public void actionPerformed(ActionEvent e) {
    Object source = e.getSource();
    if (source instanceof CreateTextbox) {
        CreateTextbox instance = (CreateTextbox) source;
        String strInput = instance.getNameText();
        String strDate = instance.getDateText();
        valiDate(strDate);
    }
}
请注意,您现在正在访问对象(类的一个实例化),而不是静态地访问类(在所有实例中都是通用的)

但是您将此操作添加到一个按钮,因此您将收到一个
JButton
作为源代码,它是您正在实现的内容的内部组件,因此这应该会让您认为您最好使用一个内部类。请注意,这通常是不正确的,也不是必需的,我是从一个好的设计角度讲的,您确实可以在外部类中实现这一点。让我添加一个建议,使用
Action
而不是
ActionListener
,这没有太大区别,但它很有用,因为它封装了整个动作,供不同组件使用,并使其在组件层次结构以及名称、图标等中可用

移动到类内部后,您可以选择声明内部类
static
,并使其独立于实例;或者不,这样您就可以访问变量。显然你的是第二个案子

@Override
public void actionPerformed(ActionEvent e) {
    String strInput = CreateTextbox.this.stringEntry.getText();
    String strDate = CreateTextbox.this.dateEntry.getText();
    valiDate(strDate);
}
这是您的方法在内部类中的外观,还原为原始代码,但注意原始的
CreateTextbox.stringEntry
(静态引用)现在是
CreateTextbox.This.stringEntry
(访问实例)

最后,它可能是这样的:

import java.awt.event.ActionEvent;
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JTextField;
import net.miginfocom.swing.MigLayout;

@SuppressWarnings("serial")
public class CreateTextbox extends JFrame {

    /* Make your inner components private, it's your responsibility to manage
     * them and so you'd not get undesired interactions */
    protected JTextField stringEntry, dateEntry;
    protected JLabel stringEntryLabel, dateEntryLabel;
    protected JButton print;
    protected Action validateAction;

    public CreateTextbox() {
        super("Create Textbox"); // The title makes more sense in the constructor
        /* Never use a null layout, is a bad practice, can work for a one-shot
         * but is in no way something you can manage or expand. Also the results
         * are usually clunky. I use MigLayout, it requires expertise but when
         * you master it it's very useful. For standard ones:
         * https://docs.oracle.com/javase/tutorial/uiswing/layout/visual.html */
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setLayout(new MigLayout("ins 10, gap 5", // Margin around of 10 px
                                                 // 5px between components 
                "[][grow]", // Fist column, minimum; second column, grow
                "[][][]"));

        /* "this." is not necessary but it's good to realize we're talking about
         * the variable of this INSTANCE of the class */
        this.stringEntryLabel = new JLabel("Name:");
        add(this.stringEntryLabel, ""); // This string is the constraint for telling
                                   // MigLayout how to place this component,
                                   // nothing required in this case 

        this.stringEntry = new JTextField();
        add(this.stringEntry, "grow, wrap"); // Fill space, next line

        this.dateEntryLabel = new JLabel("Date:");
        add(this.dateEntryLabel, "");

        this.dateEntry = new JTextField();
        add(this.dateEntry, "grow, wrap");

        this.validateAction = new MyAction();

        this.print = new JButton(this.validateAction); // Button for the action
        add(this.print, "span 2, center"); // Use 2 cells, center the button

        /* Example of how it's useful to have an action, not just a listener */
        getRootPane().getActionMap().put("validate", this.validateAction);
    }

    /* Added originally for didactical purposes */
    @Deprecated
    public String getNameText() {
        return this.stringEntry.getText();
    }

    /* Added originally for didactical purposes */
    @Deprecated
    public String getDateText() {
        return this.dateEntry.getText();
    }

    public static void main(String... args) {
        CreateTextbox me = new CreateTextbox();
        me.pack(); // Since now you have a layout, let it do its thing
        me.setLocationByPlatform(true); // Let OS place the window
        me.setVisible(true);
    }


    /* MyAction is better an Action than an ActionListener to it can be used for
     * more purposes */
    public class MyAction extends AbstractAction {

        public MyAction() {
            super("Validate");
        }

        @Override
        public void actionPerformed(ActionEvent e) {
            String strInput = CreateTextbox.this.stringEntry.getText();
            String strDate = CreateTextbox.this.dateEntry.getText();
            valiDate(strDate);
        }

        public void valiDate(String date) {
            // TODO code to validate the date
            System.out.println("Validating date \"" + date + "\"...");
        }

    }

}

让我补充一点,您应该看看这篇文章,因为Java已经有了自己的方法来验证文本字段中的日期和其他值。

给MyAction一个构造函数,它接受CreateTextbox参数并将
这个
传递给它。然后根据需要进行方法调用。字段应该是私有的,而不是公共的,并且只能通过公共方法访问。将CreateTextbox的实例传递给侦听器的构造函数。或者让侦听器成为一个内部类,能够直接引用其外部实例字段。这里可能存在内部类的重复。但最重要的是,此错误表明您的Java基础知识需要另一块砖——您必须研究静态和实例的含义以及如何将其应用于Java编程,因为这一关键知识对你的进步是绝对必要的。检查教程和你的书,并深入研究这一点。你不会后悔的。非常感谢你!我相信我理解了错误发生的原因,因为我试图从静态上下文引用非静态字段,但在阅读您的解释之前,我离遵循this.stringEntry答案只有一步之遥。谢谢你花时间把这些写出来。