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

1.6k views Asked by At

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)?
1

There are 1 answers

1
biziclop On BEST ANSWER

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.