无法将侦听器分配给类

时间:2016-09-04 09:22:55

标签: java swing inheritance nullpointerexception listener

我的问题在于莫名其妙地无法听我的一个课程。

该软件是一个基于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();
            }
        });
    }
}

2 个答案:

答案 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");
        });
    }
}