Passing data between two separate .java files
I'm learning Java and I happened to come across an ActionListener
example from Fred Swartz's leepoint.net webpage. DogYears2.java.
For the sake of learning, I experimented by seperating the ActionListener ConvertBtnListener
from the DogYears2.java
into a seperate top level class file called ConvertBtnListener.java
.
The DogYears2.java file
import java.awt.*;
import javax.swing.*;
import java.awt.event.*; // Needed for ActionListener
//////////////////////////////////////////////////////// class DogYears2
class DogYears2 extends JFrame {
//======================================================== constants
final static int DOG_YEARS_PER_HUMAN_YEAR = 7;
//=============================================== instance variables
private JTextField _humanYearsTF = new JTextField(3);
private JTextField _dogYearsTF = new JTextField(3);
//====================================================== constructor
public DogYears2() {
// 1... Create/initialize components
JButton convertBtn = new JButton("Convert");
convertBtn.addActionListener(new ConvertBtnListener());
_dogYearsTF.addActionListener(new ConvertBtnListener());
_humanYearsTF.setEditable(false);
// 2... Create content panel, set layout
JPanel content = new JPanel();
content.setLayout(new FlowLayout());
// 3... Add the components to the content panel.
content.add(new JLabel("Dog Years"));
content.add(_dogYearsTF); // Add input field
content.add(convertBtn); // Add button
content.add(new JLabel("Human Years"));
content.add(_humanYearsTF); // Add output field
// 4... Set this window's attributes, and pack it.
setContentPane(content);
pack(); // Layout components.
setTitle("Dog Year Convert开发者_高级运维er");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setLocationRelativeTo(null); // Center window.
}
//====================================================== method main
public static void main(String[] args) {
DogYears2 window = new DogYears2();
window.setVisible(true);
}
}
The ConvertBtnListener.java file
import java.awt.*;
import javax.swing.*;
import java.awt.event.*; // Needed for ActionListener
class ConvertBtnListener implements ActionListener {
JTextField _dogYearsTF;
JTextField _humanYearsTF;
int DOG_YEARS_PER_HUMAN_YEAR = 7;
public void actionPerformed(ActionEvent e) {
//... Get the value from the dog years textfield.
String dyStr = _dogYearsTF.getText();
int dogYears = Integer.parseInt(dyStr);
//... Convert it - each dog year is worth 7 human years.
int humanYears = dogYears * DOG_YEARS_PER_HUMAN_YEAR;
//... Convert to string and set human yrs textfield
_humanYearsTF.setText("" + humanYears);
}
}
Now when I ran the DogYears.java
file, the GUI pops out and when I typed in the No. of human years in the textfield, I got a NullPointerException
instead.
For the sake of learning as a Java noob, I hope somebody can enlighten me on why this happens and how do I get round this if I INSIST that I maintain a seperate ActionListener class file?
The first step is to stop thinking of files. You should be talking about classes and objects.
In your example you have defined two classes (those definitions happen to be placed in two separate files, but that doesn't really matter).
Of the first class DogYears2
(note: no ending .java
, that's only for the file) you create one instance (a.k.a one object) in your main
method.
Of the second class ConvertBtnListener
you create two instances (a.k.a two objects) in the constructor of DogYears2
.
The fields _dogYearsTF
and _humanYearsTF
in the ConvertBtnListener
class never get a value and therefore you'll get a NullPointerException
once you try to do something with them.
You should be aware that _dogYearsTF
in ConvertBtnListener
is entirely unrelated to _dogYearsTF
in DogYears2
! Furthermore, the field _dogYearsTF
in the first instance of ConvertBtnListener
is entirely unrelated to the field _dogYearsTF
n the second instance.
Now that I've told you why it doesn't work, I should probably tell you how to make it work:
One way would be to pass the necessary JTextField
objects to the constructor of the ConvertBtnListener
object. For this you'll need to create a constructor in that class that accepts the two JTextField
references and assigns them to the appropriate fields.
The usual way is to make the listener an inner class of your UI class, in which case it would have direct access to the UI classes fields and would not need such a constructor (this is why it worked in the original example).
In your listener class please initialize textfield variables. Consider this:
class ConvertBtnListener implements ActionListener {
....
ConvertBtnListener(JTextField humanYearsTF, JTextField dogYearsTF) {
_dogYearsTF = dogYearsTF;
_humanYearsTF = humanYearsTF;
}
....
}
public DogYears2() {
ActionListener listener = new ConvertBtnListener(_humanYearsTF, _dogYearsTF);
JButton convertBtn = new JButton("Convert");
...
}
see also what are anonymous classes. This is another way to implement such operations.
--- edit instead of declaring
int DOG_YEARS_PER_HUMAN_YEAR = 7;
in ConvertBtnListener class - use just static field you've declared previously in DogYears.java. So this should be right way.
int humanYears = dogYears * DogYears2.DOG_YEARS_PER_HUMAN_YEAR;
---edit constructor parameters order
In ConvertBtnListener
you get null pointer exceptions here:
String dyStr = _dogYearsTF.getText();
... and here:
_humanYearsTF.setText("" + humanYears);
You never initialize _dogYearsTF
and _humanYearsTF
in ConvertBtnListener;
these fields have nothing to do with the same name ones in DogYears2
.
you can try using the getSource() function
public void actionPerformed(ActionEvent e) {
//... Get the value from the dog years textfield.
String dyStr = _dogYearsTF.getText();
int dogYears = Integer.parseInt(dyStr);
JTextField _humanYearsTF = (JTextField)e.getSource(); // new line.
//... Convert it - each dog year is worth 7 human years.
int humanYears = dogYears * DOG_YEARS_PER_HUMAN_YEAR;
//... Convert to string and set human yrs textfield
_humanYearsTF.setText("" + humanYears);
}
This would get the source from which the action event was fired.
精彩评论