我的问题在于莫名其妙地无法听我的一个课程。
该软件是一个基于java swing的桌面应用程序,其中名为MainFrame的JFrame子类正在侦听多个对话框。所有对话框窗口都是我的PDialog类的子窗口,它带有与监听器相关的变量和函数。这就是PDialog类的样子:
public class PDialog extends JDialog {
private MainFrameChildrenListener listener;
// Function that assigns its parameter to local listener value "listener setter"
public void addMainFrameChildrenListener(MainFrameChildrenListener listener) {
this.listener = listener;
}
public void removeMainFrameChildrenListener() {
this.listener = null;
}
public void firePDialogEvent(MainFrameChildrenEventObject event) {
this.listener.dialogEventOccured(event);
}
// This method was useful when I tried to debug with System.out.println() method
public String retrieveListenerInformation(){
if(listener == null){
return "No listener loaded";
}
return this.listener.toString();
}
}
所以我创建了3个对话框,我成功地使用它们作为PDialog的子类继承的函数。 MainFrame类实现MainFrameChildrenListener侦听器对象,并将其作为侦听器传递给其构造函数中的对话框:
public class MainFrame extends JFrame implements MainFrameChildrenListener {
private PDialogCustomer dialogCustomer = new PDialogCustomer();
private PDialogOrder dialogOrder = new PDialogOrder();
private PDialogProduct dialogProduct = new PDialogProduct();
private PDialogMaterial dialogMaterial = new PDialogMaterial();
public MainFrame(){
dialogMaterial.addMainFrameChildrenListener(this);
dialogCustomer.addMainFrameChildrenListener(this);
dialogOrder.addMainFrameChildrenListener(this);
dialogProduct.addMainFrameChildrenListener(this);
System.out.println("Material dialog: " + dialogMaterial.retrieveListenerInformation());
System.out.println("Customer dialog: " + dialogCustomer.retrieveListenerInformation());
System.out.println("Order dialog: " + dialogOrder.retrieveListenerInformation());
System.out.println("Product dialog: " + dialogProduct.retrieveListenerInformation());
}
令人惊讶的是,在启动应用程序后,控制台会输出PDialog.retrieveListenerInformation()指令,这就是它的样子:
Material dialog: No listener loaded
Customer dialog: view.MainFrame[-deleted the .toString() rubbish to keep things short-]
Order dialog: view.MainFrame[-deleted the .toString() rubbish to keep things short-]
Product dialog: view.MainFrame[-deleted the .toString() rubbish to keep things short-]
如果我尝试触发监听器事件,我会得到PDialog.firePDialogEvent()方法的空指针异常。 我试图通过其构造函数将侦听器传递给PDialogMaterial类,我甚至尝试仅在PDialogMaterial类中创建新方法来传递侦听器而没有运气。我能够让事情发挥作用的唯一方法是创建新的MainFrameChildrenListener变量,该变量被声明为 public (eek!)并从MainFrame构造函数中直接访问 (eeeeek!),如此:
public class PDialogMaterial extends PDialog{
public MainFrameChildrenListener testListener;
}
public class MainFrame extends JFrame implements MainFrameChildrenListener{
public MainFrame(){
dialogMaterial.testListener = this;
dialogCustomer.addMainFrameChildrenListener(this);
dialogOrder.addMainFrameChildrenListener(this);
dialogProduct.addMainFrameChildrenListener(this);
}
}
是否有任何解释为什么所有继承相同监听器处理方法的4个类中有3个与第4个类的行为不同?我能错过什么?
评论后续:
PDialogMaterial的完整代码(不工作):
package view.dialogs;
import java.awt.Dimension;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.net.URL;
import javax.swing.DefaultComboBoxModel;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JTextField;
import view.listeners.MainFrameChildrenEventObject;
import view.listeners.MainFrameChildrenListener;
import view.utils.DoubleFormatCheck;
public class PDialogMaterial extends PDialog {
private static final long serialVersionUID = 6190469565649183032L;
// private PMaterialFabricObject fabricObject;
public static final int FABRIC_MASK = 0;
public static final int STRAP_MASK = 1;
public static final int PARTS_MASK = 2;
public static final int THREAD_MASK = 3;
private String[] fabricMaterialList = { "Cotton", "Brocate", "Satin", "Synthetic" };
private String[] fabricColorsList = { "Red", "Green", "Blue", "Purple", "Gray", "Yellow", "Black", "Khakhi",
"Carcaline" };
private MainFrameChildrenListener listener;
private GridBagConstraints gc = new GridBagConstraints();
private Dimension size = new Dimension(300, 300);
private DoubleFormatCheck dfc = new DoubleFormatCheck();
private JTextField nameField = new JTextField(8);
private JTextField priceField = new JTextField(6);
private JTextField widthField = new JTextField(6);
private JTextField vendorField = new JTextField(8);
private JButton okButton = new JButton("Ok");
private JButton cancelButton = new JButton("Cancel");
private DefaultComboBoxModel<String> fabricMaterialComboBoxModel = new DefaultComboBoxModel<String>(
fabricMaterialList);
private DefaultComboBoxModel<String> materialColorComboBoxModel = new DefaultComboBoxModel<String>(
fabricColorsList);
private JComboBox<String> fabricMaterialComboBox = new JComboBox<String>(fabricMaterialComboBoxModel);
private JComboBox<String> fabricColorComboBox = new JComboBox<String>(materialColorComboBoxModel);
public PDialogMaterial(){
}
public void addMainFrameChildrenListener(MainFrameChildrenListener listener) {
this.listener = listener;
}
public void removeMainFrameChildrenListener() {
this.listener = null;
}
public void firePDialogMaterialEventOccured(MainFrameChildrenEventObject event) {
this.listener.dialogEventOccured(event);
}
public ImageIcon getPicture(String path) {
URL link = this.getClass().getResource(path);
ImageIcon icon = new ImageIcon(link);
return icon;
}
public void setFabricMask() {
gc.gridx = 0;
gc.gridy = 0;
gc.gridwidth = 1;
gc.gridheight = 1;
gc.weightx = 1;
gc.weighty = 1;
gc.anchor = GridBagConstraints.CENTER;
// First line - picture
gc.gridwidth = 2;
this.add(new JLabel(getPicture("/images/material_32pos.gif")), gc);
gc.gridwidth = 1;
// Second line - name
gc.gridy++;
this.add(new JLabel("Name: "), gc);
gc.gridx++;
this.add(nameField, gc);
// Third line - material
gc.gridx--;
gc.gridy++;
this.add(new JLabel("Material: "), gc);
gc.gridx++;
this.add(fabricMaterialComboBox, gc);
// Fourth line - vendor name
gc.gridx--;
gc.gridy++;
this.add(new JLabel("Vendor: "), gc);
gc.gridx++;
this.add(vendorField, gc);
// Fifth line - predominating color
gc.gridx--;
gc.gridy++;
this.add(new JLabel("Predominaing color: "), gc);
gc.gridx++;
this.add(fabricColorComboBox, gc);
// Sixth line - price
gc.gridx--;
gc.gridy++;
this.add(new JLabel("Price per square meter: "), gc);
gc.gridx++;
this.add(priceField, gc);
// Seventh line - control buttons
gc.gridx--;
gc.gridy++;
this.add(okButton, gc);
gc.gridx++;
this.add(cancelButton, gc);
}
public void setStrapMask() {
gc.gridx = 0;
gc.gridy = 0;
gc.gridwidth = 1;
gc.gridheight = 1;
gc.weightx = 1;
gc.weighty = 1;
gc.anchor = GridBagConstraints.CENTER;
// First line - picture
gc.gridwidth = 2;
this.add(new JLabel(getPicture("/images/material_32pos.gif")), gc);
gc.gridwidth = 1;
// Second line - name
gc.gridy++;
this.add(new JLabel("Name: "), gc);
gc.gridx++;
this.add(nameField, gc);
// Third line - strap width
gc.gridx--;
gc.gridy++;
this.add(new JLabel("Width: "), gc);
gc.gridx++;
this.add(widthField, gc);
// Fourth line - predominating color
gc.gridx--;
gc.gridy++;
this.add(new JLabel("Predominaing color: "), gc);
gc.gridx++;
this.add(fabricColorComboBox, gc);
// Fifth line - strap price
gc.gridx--;
gc.gridy++;
this.add(new JLabel("Price per meter: "), gc);
gc.gridx++;
this.add(priceField, gc);
// Sixth line - control buttons
gc.gridx--;
gc.gridy++;
this.add(okButton, gc);
gc.gridx++;
this.add(cancelButton, gc);
}
public void constructPDialogMaterial(int maskType){
this.setLayout(new GridBagLayout());
this.setLocationRelativeTo(this.getParent());
this.setDefaultCloseOperation(PDialog.DISPOSE_ON_CLOSE);
this.setSize(size);
this.setResizable(false);
this.setVisible(false);
this.setIconImage(getPicture("/images/material_32pos.gif").getImage());
this.nameField.setBackground(this.getBackground());
this.vendorField.setBackground(this.getBackground());
this.priceField.setBackground(this.getBackground());
switch (maskType) {
case PDialogMaterial.FABRIC_MASK:
this.setTitle("Fabric material");
setFabricMask();
break;
case PDialogMaterial.STRAP_MASK:
this.setTitle("Straps");
setStrapMask();
break;
// TODO
default:
System.out.println("Oh noes! Something happend!");
}
okButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent event) {
String priceString = dfc.removeWhitespacesAndSwapCommas(priceField.getText());
if(dfc.testDoubleFormat(priceString)){
Double price = Double.valueOf(priceString);
MainFrameChildrenEventObject eventObject = new MainFrameChildrenEventObject(okButton, vendorField.getText(), nameField.getText(),
(String)materialColorComboBoxModel.getSelectedItem(), (String)materialColorComboBoxModel.getSelectedItem(),
price, PDialog.MATERIAL_EVENT_FABRIC);
firePDialogEvent(eventObject);
dispose();
}
else{
JOptionPane.showMessageDialog(PDialogMaterial.this, "Wrong price format", "Format error",
JOptionPane.WARNING_MESSAGE);
}
}
});
cancelButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent arg0) {
dispose();
}
});
}
}
PDialogCustomer的完整代码(工作示例):
package view.dialogs;
import java.awt.GridBagConstraints;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.net.URL;
import javax.swing.BorderFactory;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.JTextField;
import view.listeners.MainFrameChildrenEventObject;
public class PDialogCustomer extends PDialog {
private static final long serialVersionUID = 8431597688560531951L;
private GridBagConstraints gc = new GridBagConstraints();
private URL imageLink;
private URL iconLink;
// New customer GUI setup
JLabel nameLabel = new JLabel("Customer name: ");
JLabel addressLabel = new JLabel("Customer address: ");
JLabel dogLabel = new JLabel("Dog name: ");
JTextField nameField = new JTextField(8);
JTextField streetField = new JTextField(8);
JTextField cityField = new JTextField(8);
JTextField countryField = new JTextField(8);
JTextField dogField = new JTextField(8);
JButton okBttn = new JButton("Ok");
JButton cancelBttn = new JButton("Cancel");
// ________________________________________________________
public PDialogCustomer() {
String imagePath = "/images/customer_32pos.gif";
String iconPath = "/images/customer_16pos.gif";
this.iconLink = getClass().getResource(iconPath);
this.setIconImage(new ImageIcon(iconLink).getImage());
this.imageLink = getClass().getResource(imagePath);
this.setTitle("New customer");
nameField.setBorder(BorderFactory.createEtchedBorder());
nameField.setBackground(this.getBackground());
streetField.setBorder(BorderFactory.createTitledBorder("Street"));
streetField.setBackground(this.getBackground());
cityField.setBorder(BorderFactory.createTitledBorder("City"));
cityField.setBackground(this.getBackground());
countryField.setBorder(BorderFactory.createTitledBorder("Country"));
countryField.setBackground(this.getBackground());
dogField.setBorder(BorderFactory.createTitledBorder("Dogs name"));
dogField.setBackground(this.getBackground());
gc.weightx = 1;
gc.weighty = 1;
gc.gridheight = 1;
gc.gridwidth = 2;
gc.gridx = 0;
gc.gridy = 0;
gc.anchor = GridBagConstraints.CENTER;
add(new JLabel(new ImageIcon(imageLink)));
gc.gridwidth = 1;
gc.gridy++;
add(nameLabel, gc);
gc.gridx++;
add(nameField, gc);
gc.gridy++;
gc.gridx--;
add(addressLabel, gc);
gc.gridx++;
add(streetField, gc);
gc.gridy++;
add(cityField, gc);
gc.gridy++;
add(countryField, gc);
gc.gridy++;
gc.gridx--;
add(dogLabel, gc);
gc.gridx++;
add(dogField, gc);
gc.gridy++;
gc.gridx--;
add(okBttn, gc);
gc.gridx++;
add(cancelBttn, gc);
okBttn.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
String address = streetField.getText() + ", " + cityField.getText() + ", " + countryField.getText();
firePDialogEvent(new MainFrameChildrenEventObject(okBttn, nameField.getText(), address,
dogField.getText(), PDialog.CUSTOMER_EVENT));
dispose();
}
});
cancelBttn.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
dispose();
}
});
}
}
答案 0 :(得分:2)
违规类中的问题是它重新声明了
private MainFrameChildrenListener listener;
字段。所以你有两个私有字段,一个在超类中,一个在子类中,具有相同的名称。当您在该子类的方法中时,子类中的一个覆盖超类中的一个,但对于超类中定义的方法不可见。删除子类中的侦听器字段,它应该可以工作。
答案 1 :(得分:1)
正如Markus已经说过的那样,在你不工作的监听器中,你隐藏了监听器字段并重新声明了setter。
为了说明这个问题,我建立了一个小例子:
import java.util.Arrays;
public class HiddenField {
static abstract class Base {
String field = "BASE";
// sets the base's field
void setField(String set) {
this.field = set;
}
// ensures not to be overridden and returns the base's field.
final String getField() {
return this.field;
}
// access the field by getter (ensured to be base) and field 'field'
@Override
public String toString() {
return this.getClass().getSimpleName() + ": get()->" + getField() + " vs. field->" + field;
}
}
static class ProperExtension extends Base {
/* no need to override the base's field */
}
static class HidingExtension extends Base {
// this field isn't related to the one in Base, but has the same name!
String field = "HIDING";
// the setter is overridden (exact copy!) but because of the same-named
// field in this extending class, it sets this class's field and not the
// one in the base-class!
@Override
void setField(String set) {
this.field = set;
}
// copied the toString from above - here it is accessing the
// base-class's field via getter and this class's field directly.
@Override
public String toString() {
return this.getClass().getSimpleName() + ": get()->" + getField() + " vs. field->" + field;
}
}
public static void main(String[] args) {
// build both types of extending classes and invoke their setters
Arrays.asList(new ProperExtension(), new HidingExtension()).forEach(obj -> {
System.out.println("before ~> " + obj);
obj.setField("SET");
System.out.println("after ~> " + obj + "\n");
});
}
}