0

I've been programming with java for about 4 - 5 months now, And I have nothing to complain about. It's great. While i'm still in my learning stages, I've come pretty far, for the amount of time, i've put into it.

Ok, I've made a Calculator class. Normally, I work very neatly with Polymorphism (OO) and try to make relevant classes as possible.

Only this time, I run into problems with the buttons of the Calculator, when I separate the Calculator class into separated classes..

I.E. : What I want to do is, make an Calculator class, a ButtonHandler class, and a ActionListener class and to not write so much Operants I also want a ScriptEngineManager class (js).

But when ever I separate the Calculator java class into these classes, The buttons won't react and I get:

Exception in thread "AWT-EventQueue-0" java.lang.NullPointerException
    at calc_758D6.NumButtonActionListener.actionPerformed(NumButtonActionListener.java:29)
    at javax.swing.AbstractButton.fireActionPerformed(AbstractButton.java:2022)
    at javax.swing.AbstractButton$Handler.actionPerformed(AbstractButton.java:2346)
    at javax.swing.DefaultButtonModel.fireActionPerformed(DefaultButtonModel.java:402)
    at javax.swing.DefaultButtonModel.setPressed(DefaultButtonModel.java:259)
    at javax.swing.plaf.basic.BasicButtonListener.mouseReleased(BasicButtonListener.java:252)
    at java.awt.Component.processMouseEvent(Component.java:6527)
    at javax.swing.JComponent.processMouseEvent(JComponent.java:3321)
    at java.awt.Component.processEvent(Component.java:6292)
    at java.awt.Container.processEvent(Container.java:2234)
    at java.awt.Component.dispatchEventImpl(Component.java:4883)
    at java.awt.Container.dispatchEventImpl(Container.java:2292)
    at java.awt.Component.dispatchEvent(Component.java:4705)
    at java.awt.LightweightDispatcher.retargetMouseEvent(Container.java:4898)
    at java.awt.LightweightDispatcher.processMouseEvent(Container.java:4533)
    at java.awt.LightweightDispatcher.dispatchEvent(Container.java:4462)
    at java.awt.Container.dispatchEventImpl(Container.java:2278)
    at java.awt.Window.dispatchEventImpl(Window.java:2739)
    at java.awt.Component.dispatchEvent(Component.java:4705)
    at java.awt.EventQueue.dispatchEventImpl(EventQueue.java:746)
    at java.awt.EventQueue.access$400(EventQueue.java:97)
    at java.awt.EventQueue$3.run(EventQueue.java:697)
    at java.awt.EventQueue$3.run(EventQueue.java:691)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.security.ProtectionDomain$1.doIntersectionPrivilege(ProtectionDomain.java:75)
    at java.security.ProtectionDomain$1.doIntersectionPrivilege(ProtectionDomain.java:86)
    at java.awt.EventQueue$4.run(EventQueue.java:719)
    at java.awt.EventQueue$4.run(EventQueue.java:717)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.security.ProtectionDomain$1.doIntersectionPrivilege(ProtectionDomain.java:75)
    at java.awt.EventQueue.dispatchEvent(EventQueue.java:716)
    at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:201)
    at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:116)
    at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:105)
    at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:101)
    at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:93)
    at java.awt.EventDispatchThread.run(EventDispatchThread.java:82)

But I find this strange because I had used 'implements ActionListener' and also did I use 'extends Calculator'

so the object should be linked correctly, to work properly, right?

If someone could pls tell me how to separate this Calculator class into different classes, that would be great.

This is my code:

            package calculator;

            import java.awt.BorderLayout;
            import java.awt.Container;
            import java.awt.ComponentOrientation;
            import java.awt.Dimension;
            import java.awt.FlowLayout;
            import java.awt.GridLayout;
            import java.awt.Insets;
            import java.awt.event.ActionEvent;
            import java.awt.event.ActionListener;
            import javax.script.ScriptEngine;
            import javax.script.ScriptEngineManager;
            import javax.script.ScriptException;
            import javax.swing.BorderFactory;
            import javax.swing.JApplet;
            import javax.swing.JButton;
            import javax.swing.JComponent;
            import javax.swing.JFrame;
            import javax.swing.JTextField;
            import javax.swing.JLabel;
            import javax.swing.JOptionPane;
            import javax.swing.JPanel;
            import javax.swing.border.EmptyBorder;


            public class Calculator extends JApplet {


                // ;which is used during deserialization to verify that the sender and receiver of a serialized object have loaded classes for that object that are compatible with respect to serialization.
                public static final long serialVersionUID = 1;
                // boolean  A flag whether the text in the antwoordCalculatieEntryfield is the result of a calculation
                private boolean antwoordCalculatieEntryfield;
                //Container  The container of our app
                private Container con;
                //JPanel  The 'root' panel
                private JPanel panel;
                //JTextField  The field in which the calculations are created
                private JTextField entryField;
                //double  Memory
                private double defaultMemory = 0;
                //new
                private  Menu editDropDownMenu;
                private Menu viewDropDownMenu;
                private Menu helpDropDownMenu;
                private MenuBar menuBalk;
                private MenuItem optie1, optie2, optie3;
                private MenuItem viewOptie1, viewOptie2;
                private MenuItem helpOptie;

                /**
                 * This method is called when the app is an applet
                 * This will prepare for the app being used as an in-browser applet
                 */
                public void init() {
                    this.con = getContentPane();

                    createGUI();
                }

                /**
                 * This method is called when the app is started from the jar file
                 * This will prepare the app for being used as a standalone application
                 */
                public void go() {
                    // Create a frame and set the container
                    JFrame frame = new JFrame();
                    menuBalk = new MenuBar();
                    editDropDownMenu = new Menu ("Edit");
                    viewDropDownMenu = new Menu ("View");
                    helpDropDownMenu = new Menu ("Help");


                    this.con = frame.getContentPane();

                    // Build the GUI
                    createGUI();

                    frame.setLocation(400,400);
                    frame.setResizable(false);
                    frame.setMenuBar(menuBalk);
                    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                    frame.setSize(300, 250);
                    frame.setVisible(true);

                }

                /**
                 * Create the GUI
                 */
                private void createGUI() {
                    panel = new JPanel(new BorderLayout());

                    entryField = new JTextField("0.");
                    panel.add(entryField, BorderLayout.NORTH);

                    JPanel buttonPanel = new JPanel(new BorderLayout(10,0));

                    // Build the different panels
                    buttonPanel.add(buildNorthPanel(), BorderLayout.NORTH);
                    buttonPanel.add(buildWestPanel(),  BorderLayout.WEST);
                    buttonPanel.add(buildNumPanel(),   BorderLayout.CENTER);

                    panel.add(buttonPanel, BorderLayout.CENTER);
                    con.add(panel);

                    /**
                     * De menubalk componenten realiseren en benoemen.
                     */
                    optie1 = new MenuItem(" Copy ");
                    optie2 = new MenuItem(" Paste ");
                    optie3 = new MenuItem(" Quit  ");
                    viewOptie1 = new MenuItem("Default");
                    viewOptie2 = new MenuItem("Scientific");
                    helpOptie = new MenuItem("Info");

                    /**
                     * Toevoegen van edit/view/help componenten aan de menus.
                     */
                    editDropDownMenu.add(optie1);
                    editDropDownMenu.add(optie2);
                    editDropDownMenu.addSeparator();
                    editDropDownMenu.add(optie3);
                    viewDropDownMenu.add(viewOptie1);
                    viewDropDownMenu.add(viewOptie2);
                    helpDropDownMenu.add(helpOptie);

                    /**
                     * aan het menubalkje de edit/view/help dropdown opties toevoegen.
                     */
                    menuBalk.add(editDropDownMenu);
                    menuBalk.add(viewDropDownMenu);
                    menuBalk.add(helpDropDownMenu);


                /**
                 * Build the northPanel
                 *
                 * @return  JPanel  The created panel
                 */
                private JPanel buildNorthPanel() {
                    JPanel p = new JPanel(new FlowLayout(FlowLayout.RIGHT));

                    // We have a placeholder on the top right
                    JPanel placeHolder = new JPanel();
                    placeHolder.setPreferredSize(new Dimension(BUTTON_HEIGHT, BUTTON_HEIGHT));
                    placeHolder.setBorder(BorderFactory.createLoweredBevelBorder());
                    p.add(placeHolder);

                    createNorthButton("Backspace", p).addActionListener(new BackspaceActionListener());
                    createNorthButton("CE", p).addActionListener(new CEActionListener());
                    createNorthButton("C", p).addActionListener(new CActionListener());

                    return p;
                }

                /**
                 * Build the westPanel
                 *
                 * @return  JPanel  The created panel
                 */
                private JPanel buildWestPanel() {
                    JPanel p = new JPanel(new GridLayout(4, 1, 5, 5));
                    p.setBorder(new EmptyBorder(0, 3, 3, 0));

                    createWestButton("MC", p);
                    createWestButton("MR", p);
                    createWestButton("MS", p);
                    createWestButton("M+", p);

                    return p;
                }

                /**
                 * Build the numPanel
                 *
                 * @return  JPanel  The created panel
                 */
                private JPanel buildNumPanel() {
                    JPanel p = new JPanel(new GridLayout(4, 5, 5, 5));
                    p.setBorder(new EmptyBorder(0, 0, 4, 4));

                    createNumButton("7", p);
                    createNumButton("8", p);
                    createNumButton("9", p);
                    createNumButton("/", p);
                    createNumButton("sqrt", p);

                    createNumButton("4", p);
                    createNumButton("5", p);
                    createNumButton("6", p);
                    createNumButton("*", p);
                    createNumButton("%", p);

                    createNumButton("1", p);
                    createNumButton("2", p);
                    createNumButton("3", p);
                    createNumButton("-", p);
                    createNumButton("1/x", p);

                    createNumButton("0", p);
                    createNumButton("+/-", p);
                    createNumButton(",", p);
                    createNumButton("+", p);
                    createNumButton("=", p);

                    return p;
                }

                /**
                 * Calculate the result from a string containing a calculation
                 */
                public double calcResult(String text) {
                    // We use the build-in JavaScript engine to calculate the result
                    // this saves us a lot of weird conversions and complex calculations
                    ScriptEngineManager sem = new ScriptEngineManager();
                    ScriptEngine engine = sem.getEngineByName("JavaScript");

                    // Do some pre processing to allow javascript to do something with it
                    text = text.replace("sqrt(", "Math.sqrt(");

                    try {
                        return (double) engine.eval(text);
                    } catch (ScriptException e) {
                        JOptionPane.showMessageDialog(null, "Er was een probleem met het evalueren van uw input", "foutmelding", JOptionPane.ERROR_MESSAGE);
                    }

                    return 0.0;
                }

                /**
                 * This will run when the application is run from the jar file
                 *
                 * @param  args  Arguments
                 */
                public static void main(String[] args) {
                    Calculator app = new Calculator();
                    app.go();
                }

                /**
                 * Create a button
                 *
                 * @param   String   Contents
                 * @param   JPanel   The panel to add
                 * @return  JButton  The created button
                 */
                private JButton createButton(String contents, JPanel p) {
                    JButton b = new JButton(contents);
                    b.setMargin(new Insets(0,0,0,0));
                    p.add(b);

                    return b;
                }

                /**
                 * Create a button for the NumPanel
                 *
                 * @param   String   Contents
                 * @param   JPanel   The panel to add
                 * @return  JButton  The created button
                 */
                private JButton createNumButton(String contents, JPanel p) {
                    JButton b = createButton(contents, p);
                    b.setPreferredSize(new Dimension(BUTTON_HEIGHT, BUTTON_HEIGHT));
                    b.addActionListener(new NumButtonActionListener());

                    return b;
                }

                /**
                 * Create a button for the WestPanel
                 *
                 * @param   String   Contents
                 * @param   JPanel   The panel to add
                 * @return  JButton  The created button
                 */
                private JButton createWestButton(String contents, JPanel p) {
                    JButton b = createButton(contents, p);
                    b.setPreferredSize(new Dimension(BUTTON_HEIGHT, BUTTON_HEIGHT));
                    b.addActionListener(new MemoryActionListener());

                    return b;
                }

                /**
                 * Create a button for the NorthPanel
                 *
                 * @param   String   Contents
                 * @param   JPanel   The panel to add
                 * @return  JButton  The created button
                 */
                private JButton createNorthButton(String contents, JPanel p) {
                    JButton b = createButton(contents, p);
                    b.setPreferredSize(new Dimension(100, BUTTON_HEIGHT));

                    return b;
                }

                /**
                 * Check if string b is a number
                 *
                 * @param   String   The string to check
                 * @return  boolean  String is a number or not
                 */
                public boolean isNumber(String b) {
                    try {
                        Double.parseDouble(b);
                    } catch(NumberFormatException ex) {
                        return false;
                    }

                    return true;
                }

                /**
                 * This class handles the buttons in the 'numPanel'
                 */
                class NumButtonActionListener implements ActionListener {
                    /**
                     * Responds to a click on the button
                     *
                     * @param  ActionEvent  The event object
                     */
                    public void actionPerformed(ActionEvent ev) {
                        JButton source    = (JButton) ev.getSource();
                        String sourceText = source.getText();
                        String origText   = calcField.getText();

                        String textToAdd = "";

                        // If the text equals the default text, clear it
                        if(origText.equals(DEFAULT_TEXT)) {
                            calcField.setText("");
                        }

                        // If you just calculated something and wish to use the result
                        // we don't want to clear the text for you, the text should only
                        // be cleared when you enter a new number
                        if (fieldIsResult && isNumber(sourceText)) {
                            calcField.setText("");
                            fieldIsResult = false;
                        } else {
                            fieldIsResult = false;
                        }

                        // Some buttons have special uses
                        if(sourceText.equals("=")) { // Calculate the result
                            calcField.setText(""+calcResult(calcField.getText()));
                            fieldIsResult = true;
                        } else if(sourceText.equals("sqrt")) { // Square root needs a '('
                            textToAdd = "sqrt(";
                        } else if(sourceText.equals("+/-") && origText.length() > 0) { // Toggle negative/non-negative number
                            char firstChar = origText.charAt(0);
                            if(firstChar == '-') {
                                calcField.setText(origText.substring(1, origText.length()));
                            } else {
                                calcField.setText("-"+origText);
                            }
                        } else { // Just append the button text
                            textToAdd = sourceText;
                        }

                        if( ! textToAdd.equals("")) { // If there is something that needs to be appended
                            calcField.setText(calcField.getText() + textToAdd);
                        }
                    }
                }

                /**
                 * This class handles the backspace button
                 */
                class BackspaceActionListener implements ActionListener {
                    /**
                     * Responds to a click on the button
                     *
                     * @param  ActionEvent  The event object
                     */
                    public void actionPerformed(ActionEvent ev) {
                        String newText = calcField.getText();

                        // If there are characters
                        if(newText.length() > 0) {
                            // Remove the last one
                            newText = newText.substring(0, newText.length() - 1);
                        }

                        calcField.setText(newText);
                    }
                }

                /**
                 * This class handles the C button
                 */
                class CActionListener implements ActionListener {
                    /**
                     * Responds to a click on the button
                     *
                     * @param  ActionEvent  The event object
                     */
                    public void actionPerformed(ActionEvent ev) {
                        // C will simply clear the field
                        calcField.setText("");
                    }
                }

                /**
                 * This class handles the CE button
                 */
                class CEActionListener implements ActionListener {
                    /**
                     * Responds to a click on the button
                     *
                     * @param  ActionEvent  The event object
                     */
                    public void actionPerformed(ActionEvent ev) {
                        // C will simply clear the field
                        String newText = calcField.getText().replaceAll("[0-9]+$", "");
                        calcField.setText(newText);
                    }
                }

                /**
                 * This class handles all the memory actions
                 */
                class MemoryActionListener implements ActionListener {
                    /**
                     * Respons to a click on the button
                     *
                     * @param  ActionEvent  The event object
                     */
                    public void actionPerformed(ActionEvent ev) {
                        JButton source    = (JButton) ev.getSource();
                        String sourceText = source.getText();

                        if(sourceText.equals("MC")) {
                            memory = 0.0; // Clear memory
                        } else if (sourceText.equals("MR")) {
                            calcField.setText(""+memory);
                            fieldIsResult = true;
                        } else if (sourceText.equals("MS") && fieldIsResult) {
                            memory = Double.parseDouble(calcField.getText());
                        } else if (sourceText.equals("M+") && fieldIsResult) {
                            memory = calcResult(memory+"+"+calcField.getText());
                        }
                    }
                }
            }

And this is how for instance the class 'NumButtonActionListener' looks like, when I separate it.

public class NumButtonActionListener extends Calculator implements ActionListener {

                // boolean  A flag whether the text in the antwoordCalculatieEntryfield is the result of a calculation
                private boolean antwoordCalculatieEntryfield;
                 //JTextField  The field in which the calculations are created
                private JTextField entryField;


                /**
                 * Responds to a click on the button
                 *
                 * @param  ActionEvent  The event object
                 */

//line 25
                public void actionPerformed(ActionEvent ev) {
                    JButton source    = (JButton) ev.getSource();
                    String sourceText = source.getText();
//line 29
                    String origText   = entryField.getText();

                    String textToAdd = "";

                    // If the text equals the default text, clear it
                    if(origText.equals("0.")) {
                        entryField.setText("");
                    }

                    // If you just calculated something and wish to use the result
                    // we don't want to clear the text for you, the text should only
                    // be cleared when you enter a new number
                    if (antwoordCalculatieEntryfield && isNumber(sourceText)) {
                        entryField.setText("");
                        antwoordCalculatieEntryfield = false;
                    } else {
                        antwoordCalculatieEntryfield = false;
                    }

                    // Some buttons have special uses
                    if(sourceText.equals("=")) { // Calculate the result
                        entryField.setText(""+calcResult(entryField.getText()));
                        antwoordCalculatieEntryfield = true;
                    } else if(sourceText.equals("sqrt")) { // Square root needs a '('
                        textToAdd = "sqrt(";
                    } else if(sourceText.equals("+/-") && origText.length() > 0) { // Toggle negative/non-negative number
                        char firstChar = origText.charAt(0);
                        if(firstChar == '-') {
                            entryField.setText(origText.substring(1, origText.length()));
                        } else {
                            entryField.setText("-"+origText);
                        }
                    } else { // Just append the button text
                        textToAdd = sourceText;
                    }

                    if( ! textToAdd.equals("")) { // If there is something that needs to be appended
                        entryField.setText(entryField.getText() + textToAdd);
                    }

                }

                /**
                 * Check if string b is a number
                 *
                 * @param   b   The string to check
                 * @return  boolean  String is a number or not
                 */
                public boolean isNumber(String b) {
                    try {
                        Double.parseDouble(b);
                    } catch(NumberFormatException ex) {
                        return false;
                    }

                    return true;
                }

                /**
                 * Calculate the result from a string containing a calculation
                 */
                public double calcResult(String text) {
                    // We use the build-in JavaScript engine to calculate the result
                    // this saves us a lot of weird conversions and complex calculations
                    ScriptEngineManager sem = new ScriptEngineManager();
                    ScriptEngine engine = sem.getEngineByName("JavaScript");

                    // Do some pre processing to allow javascript to do something with it
                    text = text.replace("sqrt(", "Math.sqrt(");

                    try {
                        return (double) engine.eval(text);
                    } catch (ScriptException e) {
                        JOptionPane.showMessageDialog(null, "Er was een probleem met het evalueren van uw input", "foutmelding", JOptionPane.ERROR_MESSAGE);
                    }

                    return 0.0;
                }


                }
M D
  • 1
  • 2
  • Is there maybe a little more to the error message than what you're giving us? – pamphlet Sep 05 '14 at 14:39
  • true, I thought, that was the most important one.. I'm going to add the the full list now. – M D Sep 05 '14 at 14:42
  • Can you post the NumButtonActionListener as it is after you separated it from the Calculator class, and put a comment on line 25, that is where the error is, so that we can see that line? – Simone Gianni Sep 05 '14 at 15:08
  • @MD, the important thing you were missing was the fact that it is a NullPointerException. The type of error is always important, not just the location of it. (Just trying to help you for future debugging efforts.) – pamphlet Sep 05 '14 at 15:34
  • So, presumably entryField is null? Have you confirmed that by setting a breakpoint in actionPerformed? – pamphlet Sep 05 '14 at 15:36
  • @pamphlet yes I have, but nothing to follow after that. – M D Sep 05 '14 at 15:56
  • See [What is a stack trace, and how can I use it to debug my application errors?](http://stackoverflow.com/q/3988788/418556) & [What is a NullPointerException, and how do I fix it?](http://stackoverflow.com/q/218384/418556) – Andrew Thompson Sep 05 '14 at 16:30

1 Answers1

1

Your understanding of inheritance is all wrong

NumButtonActionListener extends Calculator

Consider the following example:

public class Foo {
    FooBoo foofoo;

    public Foo() {
        foofoo = new FooBoo();
        Bar bar = new Bar();
    }
}

public class Bar extends Foo {
    public Bar() {
        someMethod();
    }
    void someMethod() {
        foofoo.method();
    }
}

When you create an instance new Bar(), this will cause a NullPointerException because the FooBoo of the Foo class is not the same FooBoo in the Bar class. They are in no way the same reference. The FooBoo in the Bar instance has never been initialized.

You have too much code to go through, so I won't try and fix your code. But I will tell you how you can go about trying to fix this. In order for Bar to use the same FooBoo object as the one in Foo, it needs a reference to it. One way is to simply pass it to it's constructor. Something like

public class Foo {
    FooBoo foofoo;

    public Foo() {
        foofoo = new FooBoo();
        Bar bar = new Bar(foofoo);
    }
}

public class Bar {
    FooBoo foo;

    public Bar(FooBoo foo) {
        this.foo = foo;
        someMethod();
    }
    void someMethod() {
        foo.method();
    }
}

So you pass a reference of Foo's FooBoo to Bar. Then it is the same referenced FooBoo object. Notice how I didn't make Bar extend Foo. That is not a correct use of inheritance.

Paul Samsotha
  • 205,037
  • 37
  • 486
  • 720
  • wauw, makes sense. Funny thing is, I knew about this. But somehow it did not come to me, to implement it into my calculator quote. Because as One class. It did work.. Have lots to learn. Thanks ;) – M D Sep 05 '14 at 16:03
  • 2
    @M D You should accept peeskillet's answer if you found it helpful - it took effort to put that together. – Greycon Sep 05 '14 at 16:18