Problem with inner class


DevX Home    Today's Headlines   Articles Archive   Tip Bank   Forums   

Results 1 to 13 of 13

Thread: Problem with inner class

  1. #1
    Join Date
    Aug 2006
    Posts
    10

    Problem with inner class

    I'm a first-year Java student, so please go easy on me.

    I'm trying to create a GUI that allows generates a bill for mechanic services. I've defined the Swing components in an outer class, then define an inner class to do the event handling and logic. Problem is, the inner class doesn't recognize any of the components from the outer class, so I can't even get a clean compile.

    Here's the code for the GUI/processor. (I actually execute another class containing only a "main" method that instantiates the JoesAuto class.)

    ----
    import javax.swing.*;
    import java.awt.*;
    import java.awt.event.*;

    public class JoesAuto extends JFrame

    {
    final private double OIL_CHANGE_CHARGE = 26.00;
    final private double LUBE_JOB_CHARGE = 18.00;
    final private double RAD_FLUSH_CHARGE = 30.00;
    final private double TRAN_FLUSH_CHARGE = 80.00;
    final private double INSPECTION_CHARGE = 15.00;
    final private double MUFFLER_REPL_CHARGE = 100.00;
    final private double TIRE_ROTATE_CHARGE = 20.00;

    // constructor
    public JoesAuto()
    {
    // superclass constructor
    super("Service invoice");
    // instantiate a frame
    JFrame invoice = new JFrame();

    // build a panel for the routine services checkboxes
    JPanel routineServicesPanel = new JPanel();
    routineServicesPanel.setLayout(new GridLayout(7, 1));
    routineServicesPanel.setBorder(BorderFactory.createTitledBorder("Standard Services"));

    // build the routine services checkboxes
    final JCheckBox oilChange = new JCheckBox("Oil change -- $" + OIL_CHANGE_CHARGE);
    final JCheckBox lubeJob = new JCheckBox("Lube job -- $" + LUBE_JOB_CHARGE);
    final JCheckBox radFlush = new JCheckBox("Radiator flush -- $" + RAD_FLUSH_CHARGE);
    final JCheckBox tranFlush = new JCheckBox("Transmission flush -- $" + TRAN_FLUSH_CHARGE);
    final JCheckBox inspection = new JCheckBox("Inspection -- $" + INSPECTION_CHARGE);
    final JCheckBox mufflerRepl = new JCheckBox("Muffler replacement -- $" + MUFFLER_REPL_CHARGE);
    final JCheckBox tireRotate = new JCheckBox("Tire rotation -- $" + TIRE_ROTATE_CHARGE);

    // add the routine services checkboxes to the routine services panel
    routineServicesPanel.add(oilChange);
    routineServicesPanel.add(lubeJob);
    routineServicesPanel.add(radFlush);
    routineServicesPanel.add(tranFlush);
    routineServicesPanel.add(inspection);
    routineServicesPanel.add(mufflerRepl);
    routineServicesPanel.add(tireRotate);

    // place the routine services panel at the top of the frame
    add(routineServicesPanel, BorderLayout.NORTH);

    // build a panel for the custom services text fields
    JPanel customServicesPanel = new JPanel();
    customServicesPanel.setLayout(new GridLayout(2, 3));
    customServicesPanel.setBorder(BorderFactory.createTitledBorder("Custom Services"));

    // build the custom services text fields
    final JTextField customService = new JTextField("Service: ");
    final JTextField customParts = new JTextField("Parts: ");
    final JTextField customLabor = new JTextField("Labor: ");
    final JTextField customTotal = new JTextField("Total: ");
    customTotal.setEditable(false);

    // add the custom services text fields to the custom services panel
    customServicesPanel.add(customService);
    customServicesPanel.add(customParts);
    customServicesPanel.add(customLabor);
    customServicesPanel.add(customTotal);

    // place the custom services panel in the center of the frame
    add(customServicesPanel, BorderLayout.CENTER);

    // build a panel for the total and the action button
    JPanel displayPanel = new JPanel();

    // build labels and associated text/buttons
    final JLabel totalLabel = new JLabel("TOTAL: ");
    final JTextField total = new JTextField("$0000.00");
    final JButton calculate = new JButton("Calculate");

    // add a listener to the calculate button
    CalcButtonListener listener = new CalcButtonListener();
    // register the button listener
    calculate.addActionListener(listener);

    // add the display fields to the display panel
    displayPanel.add(totalLabel);
    displayPanel.add(total);
    displayPanel.add(calculate);

    // place the display panel at the bottom of the frame
    add(displayPanel, BorderLayout.SOUTH);

    // complete the frame definition
    setSize(700, 500);
    setDefaultCloseOperation(EXIT_ON_CLOSE);
    pack();
    setVisible(true);


    } // terminates JoesAuto constructor

    // build a private inner class to process the interrupt and
    // handle the logic
    // this class is still within the outer class, but not within the constructor
    private class CalcButtonListener implements ActionListener
    {

    // the interrupt handler if the calculate button is clicked
    public void actionPerformed(ActionEvent e)
    {
    // local variables
    double routineCharges = 0;
    double customPartsCharges = 0;
    double customLaborCharges = 0;
    double customTotalCharges = 0;
    double totalCharges = 0;
    JoesAuto jac = new JoesAuto();

    // accumulate charges for the routine services
    // ERROR "oilChange cannot be resolved"
    // ERROR [this error occurs with lubeJob, radFlush, tranFlush ... as well]
    if (oilChange.isSelected())
    {
    routineCharges =+ OIL_CHANGE_CHARGE;
    }
    if (lubeJob.isSelected())
    {
    routineCharges =+ LUBE_JOB_CHARGE;
    }
    if (radFlush.isSelected())
    {
    routineCharges =+ RAD_FLUSH_CHARGE;
    }
    if (tranFlush.isSelected())
    {
    routineCharges =+ TRAN_FLUSH_CHARGE;
    }
    if (inspection.isSelected())
    {
    routineCharges =+ INSPECTION_CHARGE;
    }
    if (mufflerRepl.isSelected())
    {
    routineCharges =+ MUFFLER_REPL_CHARGE;
    }
    if (tireRotate.isSelected())
    {
    routineCharges =+ TIRE_ROTATE_CHARGE;
    }

    // calculate custom charges from user entry
    // ERROR "customParts cannot be resolved
    customPartsCharges = Double.parseDouble(customParts.getText());
    // ERROR "customLabor cannot be resolved
    customLaborCharges = Double.parseDouble(customLabor.getText()) * 20.0;
    customTotalCharges = customPartsCharges + customLaborCharges;
    customTotal.setText("Total: " + customTotalCharges);

    // sum routine charges and custom charges
    totalCharges = customPartsCharges + customLaborCharges + routineCharges;

    // display summed charges
    // ERROR "displayPanel cannot be resolved
    total.setText("Total: " + totalCharges);

    } // terminates the actionPerformed method
    } // terminates the private inner class
    } // terminates JoesAuto class

    ----
    The errors are marked with comments that say ERROR.

    Can anybody help me out? I know it's just something stupid, but I can't find it.

    Thanks in advance.

  2. #2
    Join Date
    Aug 2006
    Posts
    10
    Anybody?

  3. #3
    Join Date
    Mar 2004
    Posts
    78
    Yo,

    You define a class, right? Than you want to acces some variables from the outter class ?

    Wel, there is nothing wrong with this except if the variables are not properties of the outter class. What I saw in the code is that in the constructor you create some variables. Well the inner class has no idea of them and run time it will never get any idea.

    1. Add reference to the objects you create as property of the outter class.
    2. Use the fully qualified name of the variable:


    public class JoesAuto extends JFrame {

    // some suff

    final JCheckBox oilChange;
    // or final JCheckBox oilChange = new JCheckBox.....

    // other stuff

    private class CalcButtonListener implements ActionListener {

    public void actionPerformed(ActionEvent e) {

    JoesAuto.oilChange.doAnythingYouWant(<params>);
    }
    }
    }


    And I am not sure if you do not need to add a static modifier to the inner class.

    NewUniverse

  4. #4
    Join Date
    Aug 2006
    Posts
    10
    You're exactly right. The components in the outer class were defined locally to the constructor, but were never defined to the the outer class globally. Duh.

    I got that part fixed.

    Now I'm getting runtime errors (null pointer exceptions) on the check boxes.

    What I want is for the user to be able to check the appropriate boxes for the routine services, add any custom services in the text fields, and then hit the "calculate" button and have everything tallied up. I don't want the system to calculate a total everytime a check box is checked or unchecked, so I didn't register an item listener for each checkbox component because that would trigger an event.

    Is that the right way to look at it, or will I have to have those listeners and then code the logic to handle the checkbox event(s) one way and the "calculate" event another?

    Thanks for the quick reply, by the way.

  5. #5
    Join Date
    Aug 2006
    Posts
    10
    Specifically, the runtime errors I'm getting now are:

    Exception in thread "AWT-EventQueue-0" java.lang.NullPointerException
    at JoesAuto$CheckBoxListener.itemStateChanged(JoesAuto.java:151)
    at javax.swing.AbstractButton.fireItemStateChanged(Unknown Source)
    at javax.swing.AbstractButton$Handler.itemStateChanged(Unknown Source)
    at javax.swing.DefaultButtonModel.fireItemStateChanged(Unknown Source)
    at javax.swing.JToggleButton$ToggleButtonModel.setSelected(Unknown Source)
    at javax.swing.JToggleButton$ToggleButtonModel.setPressed(Unknown Source)
    at javax.swing.plaf.basic.BasicButtonListener.mouseReleased(Unknown Source)
    at java.awt.Component.processMouseEvent(Unknown Source)
    at javax.swing.JComponent.processMouseEvent(Unknown Source)
    at java.awt.Component.processEvent(Unknown Source)
    at java.awt.Container.processEvent(Unknown Source)
    at java.awt.Component.dispatchEventImpl(Unknown Source)
    at java.awt.Container.dispatchEventImpl(Unknown Source)
    at java.awt.Component.dispatchEvent(Unknown Source)
    at java.awt.LightweightDispatcher.retargetMouseEvent(Unknown Source)
    at java.awt.LightweightDispatcher.processMouseEvent(Unknown Source)
    at java.awt.LightweightDispatcher.dispatchEvent(Unknown Source)
    at java.awt.Container.dispatchEventImpl(Unknown Source)
    at java.awt.Window.dispatchEventImpl(Unknown Source)
    at java.awt.Component.dispatchEvent(Unknown Source)
    at java.awt.EventQueue.dispatchEvent(Unknown Source)
    at java.awt.EventDispatchThread.pumpOneEventForHierarchy(Unknown Source)
    at java.awt.EventDispatchThread.pumpEventsForHierarchy(Unknown Source)
    at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
    at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
    at java.awt.EventDispatchThread.run(Unknown Source)
    ----

    ***? This happens if I 1) click any of the checkboxes;
    2) enter any of the text fields;
    3) click the calculate button.

    The "null pointer exception" is coming out of the "oilChange" checkbox (that's Line 151). From what I can deduce, the JRE is trying to fire an ItemStateChanged event when the box is clicked, which passes the event up through the abstract.buttonhandler and the DefaultButton stuff etc.. But they all say "unknown source," which indicates that the don't know where the event came from. That would lead me to believe that the event isn't registered, but all the checkboxes have listeners registered to them in the constructor.

    Is this another "duh"?

  6. #6
    Join Date
    Aug 2006
    Posts
    10
    Let me show an even simpler example. This gets the same stupid NullPointerException on the displayTotal.getText().

    ---
    import javax.swing.*;
    import java.awt.*;
    import java.awt.event.*;

    public class CheckTest extends JFrame
    {
    private JTextField test;
    private JTextField display;
    private JButton work;

    public CheckTest()
    {
    super("Services Invoice");
    JFrame testFrame = new JFrame();
    JTextField test = new JTextField(10);
    JTextField display = new JTextField(10);
    JButton work = new JButton("work");
    JPanel testPanel = new JPanel();
    testPanel.setLayout(new GridLayout(3, 1));
    testPanel.add(test);
    testPanel.add(display);
    testPanel.add(work);
    add(testPanel, BorderLayout.NORTH);

    WorkButtonListener listener = new WorkButtonListener();
    work.addActionListener(listener);

    setSize(700, 500);
    setDefaultCloseOperation(EXIT_ON_CLOSE);
    pack();
    setVisible(true);
    }

    private class WorkButtonListener implements ActionListener
    {

    // the interrupt handler if the calculate button is clicked
    public void actionPerformed(ActionEvent e)
    {
    String displayTest = new String("");
    displayTest = test.getText();
    display.setText(displayTest);

    }
    }
    }

    ----

    This is not rocket science, yet the event handler seems to choke and puke on it every time.

  7. #7
    Join Date
    Aug 2006
    Posts
    10
    If I define the global components as "final," the NullPointerExceptions go away. But nothing happens when the class runs. I can enter a value in the "test" field, and the test.getText() method doesn't return anything. So the display.setText() doesn't do a thing. In fact, those fields never change (which you would expect from fields declared as "final").

    This shouldn't be this hard.

  8. #8
    Join Date
    Mar 2004
    Posts
    78
    Hi there,

    You build a software that follows a specific logic. So... follow the logic.

    Take a sheet of paper and start to draw:
    1) what happens - example the user hits a button
    2) what should my software do - example nothing or calculate or anything else

    Then you need to add listener to the control that should rise the apprpriate event. In the example it is the button.

    When you get the message you need to find you source data - the event will let you find the source control and the outer class also provides you with access to the components.

    If you have the data - calculate and store the results wherever you need to. Just be sure tha you follow the AWT/Swing way to red/write data to the controls. Awt/SWing is a message driven framework so the approproiate message must be thrown or initiated.

    This is it.

    NewUniverse

  9. #9
    Join Date
    Aug 2006
    Posts
    10
    I appreciate your response, but I'm not sure what you're telling me. I already have an outer class. I already have a listener. I already have the listener registered to trigger an event. I process the event when it is triggered. I shouldn't have to do a getSource to figure out what component triggered the event; there's only one that CAN.

    So where does the NullPointerException come from? What object is undefined at runtime, when the action event is triggered? It has to be the "test" text field, since that's where the error is being flagged, but there's nothing wrong with the "test" text field definition.

    So why does this error keep coming up??????

  10. #10
    Join Date
    Mar 2004
    Posts
    78
    Hey...

    Look throw the code AGAIN.

    It is so obvious.

    You create an instance in the constructor and add it to the Swing panel, frame whatever . Well, this is what you have to do but... do you let the outter class know about it ? Do you have line:

    this.test = test;

    ?!?!?!?!?!?!??!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

    Why in constructor do not just write: test = new .... ?

    You will get NullPointer until you do not assign something to your the test class property )

    The computer can not read you thoughts(like me) just your code ))))

    NewUniverse

  11. #11
    Join Date
    Aug 2006
    Posts
    10
    I removed the JTextField test = new JTextField(10) declaration from the constructor and replaced it with test = new JTextField(10). You were 100 percent correct; the NullPointerExceptions went away. And you're equally correct in saying that it was obvious all along. Thank you very much for your insight.

    Do you have time for one more? This one makes even less sense than the previous one.

    It's back to the class that started all this, JoesAuto. I broke it up into three separate classes. I used one for the driver, and separate classes to define the panels for each component group. The GUI works fine. But when I try to add the ActionListener to the calcButton, I get "ActionListener cannot be resolved to a type" and "ActionEvent cannot be resolved to a type." I cut-and-pasted the syntax directly from this forum's documentation on How to Write an Action Listener, so I know it's not the syntax. I have imported java.awt.* and javax.swing.*, so the ActionListener classes are there. But it seems the compiler doesn't recognize ActionListener and ActionEvent as valid constructs.

    I'll include the code in the next reply; it's on another machine right now.

    By the way, I'm developing with Eclipse, if that has anything to do with it.

    Again, thank you very much for your time. I'm sure I must sound like a complete idiot, but actually I love programming this language.

  12. #12
    Join Date
    Aug 2006
    Posts
    10
    DRIVER CLASS
    ----
    import java.awt.*;
    import java.awt.event.ActionListener.*;
    import javax.swing.*;
    import javax.swing.event.*;

    public class JoesAuto extends JFrame
    {

    private RoutinePanel routine;
    private CustomPanel custom;
    private JPanel displayPanel;

    private JButton calcButton;

    public JoesAuto()
    {
    super("Joes Auto Service Invoice");

    setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

    routine = new RoutinePanel();
    custom = new CustomPanel();

    buildButtonPanel();

    add(routine, BorderLayout.NORTH);
    add(custom, BorderLayout.CENTER);
    add(displayPanel, BorderLayout.SOUTH);

    pack();
    setVisible(true);
    }

    public void buildButtonPanel()
    {
    displayPanel = new JPanel();
    calcButton = new JButton("Calculate");
    ButtonListener buttonListener = new ButtonListener();
    // calcButton.addActionListener(buttonListener)
    displayPanel.add(calcButton);
    }

    // ERROR "ActionListener cannot be resolved to a type"
    private class ButtonListener implements ActionListener
    {

    calcButton.addActionListener(this);
    // ERROR "ActionEvent cannot be resolved to a type"
    public void actionPerformed(ActionEvent e)
    {


    }
    }

    public static void main(String[] args)
    {
    JoesAuto ja = new JoesAuto();
    }

    }

    CONTAINER CLASSES
    ----
    import java.awt.*;
    import javax.swing.*;

    public class RoutinePanel extends JPanel
    {
    private final double OIL_CHANGE_CHARGE = 26.0;
    private final double LUBE_JOB_CHARGE = 18.0;
    private final double RAD_FLUSH_CHARGE = 30.0;
    private final double TRAN_FLUSH_CHARGE = 80.0;
    private final double INSPECTION_CHARGE = 15.0;
    private final double MUFFLER_REPL_CHARGE = 100.0;
    private final double TIRE_ROTATE_CHARGE = 20.0;

    private JCheckBox oilChange;
    private JCheckBox lubeJob;
    private JCheckBox radFlush;
    private JCheckBox tranFlush;
    private JCheckBox inspection;
    private JCheckBox mufflerRepl;
    private JCheckBox tireRotate;

    public RoutinePanel()
    {
    oilChange = new JCheckBox("Oil Change -- $" + OIL_CHANGE_CHARGE + "0");
    lubeJob = new JCheckBox("Lube Job -- $" + LUBE_JOB_CHARGE + "0");
    radFlush = new JCheckBox("Radiator Flush -- $" + RAD_FLUSH_CHARGE + "0");
    tranFlush = new JCheckBox("Transmission Flush -- $" + TRAN_FLUSH_CHARGE + "0");
    inspection = new JCheckBox("Inspection -- $" + INSPECTION_CHARGE + "0");
    mufflerRepl = new JCheckBox("Muffler Replacement -- $" + MUFFLER_REPL_CHARGE + "0");
    tireRotate = new JCheckBox("Tire Rotation -- $" + TIRE_ROTATE_CHARGE + "0");

    setLayout(new GridLayout(7, 1));

    setBorder(BorderFactory.createTitledBorder("Routine Services"));

    add(oilChange);
    add(lubeJob);
    add(radFlush);
    add(tranFlush);
    add(inspection);
    add(mufflerRepl);
    add(tireRotate);
    }

    public double getCharges()
    {
    double charges = 0;

    if (oilChange.isSelected())
    {
    charges =+ OIL_CHANGE_CHARGE;
    }
    if (lubeJob.isSelected())
    {
    charges =+ LUBE_JOB_CHARGE;
    }
    if (radFlush.isSelected())
    {
    charges =+ RAD_FLUSH_CHARGE;
    }
    if (tranFlush.isSelected())
    {
    charges =+ TRAN_FLUSH_CHARGE;
    }
    if (inspection.isSelected())
    {
    charges =+ INSPECTION_CHARGE;
    }
    if (mufflerRepl.isSelected())
    {
    charges =+ MUFFLER_REPL_CHARGE;
    }
    if (tireRotate.isSelected())
    {
    charges =+ TIRE_ROTATE_CHARGE;
    }

    return charges;
    }

    }

    ----
    import java.awt.*;
    import javax.swing.*;

    public class CustomPanel extends JPanel
    {
    private final double LABOR_RATE = 20.0;

    private JTextField partsCost;
    private JTextField laborTime;



    public CustomPanel()
    {
    JLabel partsLabel = new JLabel("Parts");
    JLabel laborLabel = new JLabel("Shop Labor (hrs)");
    JTextField partsCost = new JTextField("0");
    JTextField laborTime = new JTextField("0");


    // partsCost.setText("0");
    // laborTime.setText("0");

    setLayout(new GridLayout(2, 2));

    setBorder(BorderFactory.createTitledBorder("Custom Services"));

    add(partsLabel);
    add(laborLabel);
    add(partsCost);
    add(laborTime);
    }

    public double getCharges()
    {
    double totalCharges = 0.0;
    double partsCharges = 0.0;
    double laborDouble = 0.0;
    double laborCharges = 0.0;

    partsCharges = Double.parseDouble(partsCost.getText());
    laborDouble = Double.parseDouble(laborTime.getText());
    laborCharges = laborDouble * LABOR_RATE;

    totalCharges = partsCharges + laborCharges;
    return totalCharges;
    }

    }
    ----

    I don't see anything wrong with the ActionListener/Event declarations. Do you?

  13. #13
    Join Date
    Aug 2006
    Posts
    10
    I got it figured out. I had imported java.awt.event.*, but apparently the compiler is picky enough it needs java.awt.event.ActionListener and java.awt.event.ActionEvent specifically. When I added those imports the "unable to resolve to a type" errors went away.

    I still don't understand how the ActionListener and ActionEvent are not included in the java.awt.event.* package, but I'm assuming it has something to do with the way my Eclipse is configured.

Similar Threads

  1. Assembly class
    By Shailesh C.Rathod in forum .NET
    Replies: 2
    Last Post: 03-13-2002, 08:53 PM
  2. Replies: 6
    Last Post: 06-19-2001, 12:47 AM
  3. How To Do It - Shared Class Variables Part III
    By Patrick Ireland in forum .NET
    Replies: 5
    Last Post: 05-10-2001, 07:19 PM
  4. How To Do It - Shared Class Variables Part IV
    By Patrick Ireland in forum .NET
    Replies: 3
    Last Post: 05-07-2001, 04:04 PM
  5. problem with zipinputstream class
    By dinesh in forum Java
    Replies: 1
    Last Post: 11-29-2000, 12:19 PM

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  
HTML5 Development Center
 
 
FAQ
Latest Articles
Java
.NET
XML
Database
Enterprise
Questions? Contact us.
C++
Web Development
Wireless
Latest Tips
Open Source


   Development Centers

   -- Android Development Center
   -- Cloud Development Project Center
   -- HTML5 Development Center
   -- Windows Mobile Development Center