我可以在运行时查询 lambda 函数的生命周期吗?

问题描述

我知道 Java 编译器会根据上下文和闭包为 lambda 函数生成不同的类。当我接收 lambda 作为参数时(使用 Consumer<> 类),我可以知道参数的生命周期吗?

例如,我有以下 Observable 类,它保持对其观察的弱引用

class Observable {
    private final List<WeakReference<Consumer<Object>>> observables = new ArrayList<>();
    private Object obj;

    public Observable(Object obj){
        this.obj = obj;
    }

    public void observe(Consumer<Object> cons){
        this.observables.add(new WeakReference<>(cons));
    }

    public void set(Object obj){
        this.obj = obj;
        // notify observes
        for(WeakReference<Consumer<Object>> cons : this.observables){
            if(cons.get() != null)
                cons.get().accept(this.obj);
            // clearing the non-existing observes from the list is ommited for simplicity
        }
    }
}

现在我是这样使用的。

public class Main {
    public static void main(String[] args) {
        Object c = new Object();
        Observable obs = new Observable(c);

        new ContainingClass(obs);
        obs.set(c);
        System.gc();
        obs.set(c);
    }
}

代码只是创建了对象及其观察者,并创建了观察者的ContainingClass(定义如下)。然后对象设置一次,垃圾收集器显式调用(因此创建的 ContainingClass 被删除)并再次设置对象。

现在,只要 lambda 是特定于实例的(引用 this 或其实例方法),它就只会被调用一次(因为它被 GC 销毁了)。

public class ContainingClass {
    public ContainingClass(Observable obs){
        obs.observe(this::myMethod);
    }

    private void myMethod(Object obj) {
        System.out.println("Hello here");
    }
}
public class ContainingClass {
    private Object obj;

    public ContainingClass(Observable obs){
        obs.observe(obj -> {
            this.obj = obj;
            System.out.println("Hello here");
        });
    }
}

但是当 lambda 变为静态时,它会被调用两次,即使在 GC 之后也是如此。

public class ContainingClass {
    public ContainingClass(Observable obs){
        obs.observe((obj) -> System.out.println("Hello here"));
    }
}

对这个 lambda 的引用永远不会被销毁,因此每次创建 ContainingClass 实例时都会添加为观察者。结果,它会一直卡在观察者中,直到程序结束。

有没有办法检测到这一点并至少显示一个警告,即永远不会删除 lambda?

我发现的一件事是,具有实例生命周期的 lambda 具有 arg$1 属性,因此我可以询问属性数量

public void observe(Consumer<Object> cons){
    if(cons.getClass().getDeclaredFields().length == 0)
        System.out.println("It is static lifetime lambda");
    this.observables.add(new WeakReference<>(cons));
}

这是一种通用方法吗?可能会出现这种情况不起作用的情况吗?

解决方法

您的问题类似于this question,因此此处的答案也适用。

静态嵌套类有一个可以检查的标志:

 Modifier.isStatic(clazz.getModifiers())   // returns true if a class is static,false if not
,

我认为一个很好的解决方案是@Olivier 提示的:您可以使用 remove 方法返回一个对象,该方法在调用时从列表中删除您的 Consumer,如下例所示:

@FunctionalInterface
public interface Registration {
    void remove();
} 
class Observable {
    private final List<Consumer<Object>> observables = new ArrayList<>();
    private Object obj;

    public Observable(Object obj) {
        this.obj = obj;
    }

    public Registration observe(Consumer<Object> cons) {
        this.observables.add(cons);
        return () -> this.observables.remove(cons);
    }

    public void set(Object obj) {
       [...]
    }
}

另一种方法是检查 lambda 所属的类是否是静态的,正如@kutschkem 所建议的那样,但如果有好的选择,我不喜欢诉诸内省。

正如@shalk 已经指出的那样,依赖 WeakReference 来处理 GC 会导致不需要的行为,因为无法确保您的 Consumer 没有在某处被引用(可能是错误的)否则。

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...