Search code examples
javalambdajavafxweak-references

How to correctly create a weak reference to method reference in Java


I want to register a JavaFX ListChangeListener to an ObservableList. However I noticed, that under certain circumstances the Listener is not getting called.

(1) If the Listener is a method reference, everything works:

// using a direct method reference:
private final ListChangeListener<String> listener = this::listDidChange;

/* ... */
public void init() {
    list.addListener(listener);
}

(2) If however the Listener is a weak ref to the same method, the listener is NOT invoked:

// using a weak method reference:
private final ListChangeListener<String> listener = new WeakListChangeListener<String>(this::listDidChange);

/* ... */
public void init() {
    list.addListener(listener);
}

(3) Now the really funny part is, that this is working again, even though it should be the same as the previous example:

// direct method reference wrapped into a weak ref later:
private final ListChangeListener<String> listener = this::listDidChange;

/* ... */
public void init() {
    list.addListener(new WeakListChangeListener<String>(listener));
}

Two questions:

  • What exactly happens, when a weak ref to a method ref is created?
  • What is the difference between (2) and (3)?

Solution

  • Creating a method reference (in this case) is just like creating any other object. So if we replace it with a new expression, example 2 becomes like:

    private final ListChangeListener<String> listener = new WeakListChangeListener<String>(new Foo());
    
    public void init() {
        list.addListener(listener);
    }
    

    Whereas example 3 would be:

    // direct method reference wrapped into a weak ref later:
    private final ListChangeListener<String> listener = new Foo();
    
    public void init() {
        list.addListener(new WeakListChangeListener<String>(listener));
    }
    

    Now the difference becomes fairly obvious:

    1. In example 2 the newly created instance of Foo is instantly eligible for garbage collection, as nothing holds a strong reference to it.
    2. In example 3 you keep a strong reference to the newly created Foo object in your listener field, so it will only be collected when the object with the listener field is collected.

    P.s.: If method references in Java really were references to methods, meaning that methods were first-class objects in their own right (like in Javascript), example 2 would work too as every object would implicitly hold a reference to all their methods.