Search code examples
javanetbeansnullpointerexceptionpropertychangelistenerpropertychangesupport

Adding a PropertyChangeListener leads to java.lang.NullPointerException


I'm new to working with Netbeans and PropertyChangeListeners and I got same (for me) strange behavior when implementing a PropertyChangeSupport in a Java bean.

So I have one bean called TTTCell in which I initialize a PropertyChangeSupport variable. I then implement the functions to maintain the the property change listener list.

package tttboard;

import java.awt.Color;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.beans.PropertyVetoException;
import java.beans.VetoableChangeListener;
import java.beans.VetoableChangeSupport;

public class TTTCell extends javax.swing.JPanel {

    public static enum State
    {
        INITIAL,
        PLAYER_X,
        PLAYER_O,
        WON,
        DISABLE
    }
    
    // Variables
    private State state;
    private PropertyChangeSupport propChange = new PropertyChangeSupport(this);
    
    public TTTCell() {
        
        initComponents();
        state = State.INITIAL;
    }
    
    @Override
    public void addPropertyChangeListener(PropertyChangeListener listener) {
        propChange.addPropertyChangeListener(listener); // it depends on that line whether the erroroccurs or not
    }
    @Override
    public void removePropertyChangeListener(PropertyChangeListener listener) {
        propChange.removePropertyChangeListener(listener);
    }
    

I now put this bean in another bean called TTTBoard and it gives me the following error:

Exception in thread "AWT-EventQueue-0" java.lang.NullPointerException
    
at tttboard.TTTCell.addPropertyChangeListener(TTTCell.java:55)
    at java.desktop/javax.swing.plaf.synth.SynthPanelUI.installListeners(SynthPanelUI.java:83)
    at java.desktop/javax.swing.plaf.synth.SynthPanelUI.installUI(SynthPanelUI.java:63)
    at java.desktop/javax.swing.JComponent.setUI(JComponent.java:685)
    at java.desktop/javax.swing.JPanel.setUI(JPanel.java:150)
    at java.desktop/javax.swing.JPanel.updateUI(JPanel.java:126)
    at java.desktop/javax.swing.JPanel.<init>(JPanel.java:86)
    at java.desktop/javax.swing.JPanel.<init>(JPanel.java:109)
    at java.desktop/javax.swing.JPanel.<init>(JPanel.java:117)
    at tttboard.TTTCell.<init>(TTTCell.java:38)
    at tttboard.TTTBoard.initComponents(TTTBoard.java:89)
    at tttboard.TTTBoard.<init>(TTTBoard.java:27)
    at tttboard.TTTBoard$2.run(TTTBoard.java:203)
    at java.desktop/java.awt.event.InvocationEvent.dispatch(InvocationEvent.java:313)
    at java.desktop/java.awt.EventQueue.dispatchEventImpl(EventQueue.java:770)
    at java.desktop/java.awt.EventQueue$4.run(EventQueue.java:721)
    at java.desktop/java.awt.EventQueue$4.run(EventQueue.java:715)
    at java.base/java.security.AccessController.doPrivileged(Native Method)
    at java.base/java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:85)
    at java.desktop/java.awt.EventQueue.dispatchEvent(EventQueue.java:740)
    at java.desktop/java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:203)
    at java.desktop/java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:124)
    at java.desktop/java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:113)
    at java.desktop/java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:109)
    at java.desktop/java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:101)
    at java.desktop/java.awt.EventDispatchThread.run(EventDispatchThread.java:90)

For me that does not make any sense - the PropertyChangeSupport variable is instantiated, why should it be a Null Pointer?

FYI I'm using Netbeans IDE 11.1 and openjdk 11.0.11


Solution

  • The non-static fields of a class are not initialized until that class enters its initialization/construction. Superclass construction always takes place first.

    If you look at the stack trace, you can see that the exception occurred as the result of superclass construction:

    at java.desktop/javax.swing.JPanel.<init>(JPanel.java:117)
    at tttboard.TTTCell.<init>(TTTCell.java:38)
    

    Before the TTTCell field initialization and constructor can run, the superclass constructor (that is, the JPanel constructor) must complete.

    The JPanel constructor calls its updateUI method, which eventually calls addPropertyChangeListener:

    at tttboard.TTTCell.addPropertyChangeListener(TTTCell.java:55)
    at java.desktop/javax.swing.plaf.synth.SynthPanelUI.installListeners(SynthPanelUI.java:83)
    at java.desktop/javax.swing.plaf.synth.SynthPanelUI.installUI(SynthPanelUI.java:63)
    at java.desktop/javax.swing.JComponent.setUI(JComponent.java:685)
    at java.desktop/javax.swing.JPanel.setUI(JPanel.java:150)
    at java.desktop/javax.swing.JPanel.updateUI(JPanel.java:126)
    at java.desktop/javax.swing.JPanel.<init>(JPanel.java:86)
    

    But TTTCell hasn’t had a chance to run its initializers, which includes not only the TTTCell() constructor, but also the initialization of fields, including this one:

    private PropertyChangeSupport propChange = new PropertyChangeSupport(this);
    

    Since that line has not run yet (because the JPanel constructor still hasn’t finished) at the time your addPropertyChangeListener override is called, propChange is still null.

    The solution is an easy one: remove your overrides of addPropertyChangeListener and removePropertyChangeListener. They provide no benefit whatsoever. The Component class already provides a working implementation of each of those methods. There is nothing stopping you from using them.