Be careful with method references in JavaFX 8!


dejv bio photo By dejv

I’ve just discovered an interesting problem in my JavaFX code, regarding the add/removeEventHandler(Filter) mechanism, when used in conjunction with Java 8 method references.

If one thinks about it for a moment, it isn’t a bug or something, the handling is actually correct, but nevertheless, it can easily lead to very confusing errors. Here is how:

In Java 7 / JavaFX 2.x, I used the following construct to register and unregister event handlers (filters):

private final EventHandler<mouseevent> handler = new EventHandler<mouseeevent>() {

    @Override
    public void handle(MouseEvent e) {
        // Do something...
    }
};

...

// Register the handler:
node.addEventHandler(MouseEvent.MOUSE_CLICKED, handler);

// Unregister the handler:
node.removeEventHandler(MouseEvent.MOUSE_CLICKED, handler);

However, with the introduction of Java 8, I took advantage of the new, shiny method reference feature instead, to shorten my code:

private void handleMouseClick(MouseEvent e) {
    // Do something...
}

...

// Register the handler:
node.addEventHandler(MouseEvent.MOUSE_CLICKED, this::handleMouseClick);

// Unregister the handler:
node.removeEventHandler(MouseEvent.MOUSE_CLICKED, this::handleMouseClick);

However, suddenly I realized, that the “Mouse Click” notifications are still coming, even after the call to “removeEventHandler”. Deeper inspection revealed, that with each call to add / remove… method pair, there’s actually one more copy of the handler registered, so the same notification even starts to arrive multiple times!

The magic lies obviously in the fact, that each reference to “handleMouseClick” method, actually creates a brand-new instance of the EventHandler functional interface under the hood, so the one, that gets registered is completely unrelated to the one, that gets unregistered. But this is not really apparent from the code, and on top of that, the “removeEventHandler” method consumes the previously unregistered handler without any complaint, so the problem may easily pass unnoticed for quite a long time (half a year in my case :))

So, in case you’ve experienced some strange behavior with add/removeEventHandler(resp. Filter), you might like to double-check, that you are really registering and unregistering the same reference :)