在为Java Swing元素实现ActionListener时,如何解决静态引用错误?
我是一个相当新手的Java程序员,正在尝试使用JavaSwing元素创建一个简单的用户界面,该元素接受用户输入,并(最终)验证输入的日期的格式是否正确 我目前正在尝试使用一个主“createTextbox”类,该类生成表单,同时使用一个实现ActionListener的类来读取用户的输入,并在单击按钮时验证日期。我遇到的问题是,当我尝试在ActionListener类中从CreateTextbox引用我的字段时,我得到了一个错误,因为在静态上下文中引用我的字段是非静态的。但是,我不想将字段定义为静态字段,因为我希望用户能够更改输入并再次单击按钮 以下是我的CreateTextbox类:在为Java Swing元素实现ActionListener时,如何解决静态引用错误?,java,swing,Java,Swing,我是一个相当新手的Java程序员,正在尝试使用JavaSwing元素创建一个简单的用户界面,该元素接受用户输入,并(最终)验证输入的日期的格式是否正确 我目前正在尝试使用一个主“createTextbox”类,该类生成表单,同时使用一个实现ActionListener的类来读取用户的输入,并在单击按钮时验证日期。我遇到的问题是,当我尝试在ActionListener类中从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答案只有一步之遥。谢谢你花时间把这些写出来。