如何在Java中正确创建一个方法引用的弱引用

3

我希望注册一个JavaFX ListChangeListener 到一个 ObservableList。然而,我注意到在某些情况下,监听器没有被调用。

(1) 如果监听器是一个方法引用,那么一切正常:

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

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

(2) 然而,如果监听器是对同一方法的弱引用,则不会调用该监听器:

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

/* ... */
public void init() {
    list.addListener(listener);
}
(3) 现在真正有趣的部分是,尽管它应该与前面的示例相同,但它仍然可以工作:
// 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));
}

两个问题:

  • 创建一个指向方法引用的弱引用时,究竟会发生什么?
  • (2)和(3)之间有什么区别?

5
根据WeakListChangeListener的JavaDoc:“注意:只要使用ListChangeListener,就必须保留传递进来的引用,否则它会被过早地垃圾回收。” - neuronaut
1个回答

11

在这种情况下,创建方法引用就像创建任何其他对象一样。因此,如果我们将其替换为new表达式,示例2将变为:

创建方法引用(在本例中)就像创建任何其他对象一样。 因此,如果我们将其替换为new表达式,则示例2将变为:

private final ListChangeListener<String> listener = new WeakListChangeListener<String>(new Foo());

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

而第三个示例将是:

// 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));
}

现在的区别变得非常明显:

  1. 在示例2中,新创建的Foo实例立即符合垃圾回收条件,因为没有任何东西持有它的强引用。
  2. 在示例3中,您在listener字段中保留对新创建的Foo对象的强引用,因此只有在具有listener字段的对象被收集时,该对象才会被收集。

附言:如果Java中的方法引用确实是对方法的引用,这意味着方法本身就是一等公民(像Javascript中那样),则示例2也可以工作,因为每个对象都会隐式持有对其所有方法的引用。


1
重要的是要強調,即使固定版本也取決於周圍對象(具有“listener”實例字段的對象)的可達性。原則上,如果它們的唯一根引用是局部變量,優化JVM可以消除整個對象圖。即使“Java中的方法引用真的是對方法的引用”,這也適用,並證明允許不可預測的垃圾回收改變程序的語義(即是否會調用事件處理程序)不是一個好主意。 - Holger

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接