Subclass of abstract JDialog -> NullPointerException
Hello!
I wanted to make an abstract subclass of JDialog that will handle a few things that are common to several of the dialogs in my program. Things like creating and setting up the Ok/Cancel buttons, handling the case where the user closes the dialog with the red X as though they clicked Cancel, etc. The method that is called when the Ok button is clicked is abstract and is supposed to be implemented by the subclasses.
The problem is, when the subclass implements that ok() method and tries to access one of its own instance variables (a JTextField), it throws a NullPointerException. I'm certain this JTextField instance was created, because it was added to the dialog's content pane; I can see it and type text into it. The reference is also visible across method calls after it's created. But when the Ok button is clicked and the method tries to access it, it's always null for some reason that I can't figure out. I tried refactoring the inner classes to their own class files, but it gave me the same results.
Here's a SSCCE. I tried to keep it as short as possible. The comments should highlight the relevant parts of the code. I can do this a few other ways, so I'm not really looking for a JDialog tutorial or anything. I'm mainly just curious why this doesn't work. Can anyone tell me what I'm doing wrong here?
import java.awt.BorderLayout;
import java.awt.EventQueue;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTextField;
publicclass Main{
// This creates and shows JFrame with a button. Clicking the
// button displays a SomeDialog instance that prompts the user
// to enter a String value.
publicstaticvoid main(String[] args){
final JFrame frame =new JFrame();
final JButton btnShowDialog =new JButton("Show Dialog");
frame.setSize(340,280);
frame.setLocationRelativeTo(null);
frame.getContentPane().add(btnShowDialog, BorderLayout.SOUTH);
frame.addWindowListener(new WindowAdapter(){
publicvoid windowClosing(WindowEvent e){
frame.dispose();
}
});
btnShowDialog.addActionListener(new ActionListener(){
publicvoid actionPerformed(ActionEvent e){
SomeDialog dlg =new SomeDialog(frame);
dlg.setVisible(true);
if (dlg.isCancelled() ==true){
System.out.println("Cancelled");
}else{
String value = dlg.getValue();
System.out.println("Value: "+value);
}
}
});
EventQueue.invokeLater(new Runnable(){
publicvoid run(){
frame.setVisible(true);
}
});
}
// This is intended to provide some basic dialog behavior.
privatestaticabstractclass AbstractDialogextends JDialog{
privateboolean cancelled =false;
private JButton btnOk =new JButton("Ok");
private JButton btnCancel =new JButton("Cancel");
protectedabstractvoid initComponents();
protectedabstractvoid ok();
public AbstractDialog(JFrame parent){
super(parent,true);
addWindowListener(new WindowAdapter(){
publicvoid windowClosing(WindowEvent e){
cancelled =true;
}
});
// This call to initComponents() should cause the subclass to
// create its txtValue instance long before the Ok button is
// clicked.
initComponents();
pack();
setLocationRelativeTo(parent);
}
protected JPanel buildButtons(){
btnOk.addActionListener(new ActionListener(){
publicvoid actionPerformed(ActionEvent e){
cancelled =false;
ok();
}
});
btnCancel.addActionListener(new ActionListener(){
publicvoid actionPerformed(ActionEvent e){
cancelled =true;
dispose();
}
});
JPanel pnlGrid =new JPanel(new GridLayout(1,2,5,5));
pnlGrid.add(btnOk);
pnlGrid.add(btnCancel);
return pnlGrid;
}
publicboolean isCancelled(){
return cancelled;
}
}
// This implements AbstractDialog to prompt the user for a
// String value.
privatestaticclass SomeDialogextends AbstractDialog{
// txtValue is a class-level instance in the subclass.
private JTextField txtValue =null;
private String value =null;
public SomeDialog(JFrame parent){
super(parent);
}
@Override
protectedvoid initComponents(){
// The class-level JTextField instance is created here.
// I would expect this instance to persist throughout the
// life of this dialog.
txtValue =new JTextField();
getContentPane().add(txtValue, BorderLayout.NORTH);
getContentPane().add(buildButtons(), BorderLayout.SOUTH);
// This check is to make sure that the txtValue reference
// is non-null across method calls. It always prints
// "Not null"
checkNull();
}
privatevoid checkNull(){
if (txtValue ==null){
System.out.println("Null");
}else{
System.out.println("Not null");
}
}
@Override
protectedvoid ok(){
// *** The NullPointerException is thrown at this line: ***
value = txtValue.getText();
dispose();
}
public String getValue(){
return value;
}
}
}

