确定 lambda 表达式在 Java 中是无状态的还是有状态的

2022-09-02 10:32:15

是否有一个函数接受对 lambda 表达式的引用并返回一个布尔值,说明 lambda 表达式是否为无状态?如何确定 lambda 表达式的有状态性?


答案 1

好吧,lambda 表达式只是一个只有一个方法的特殊匿名类的实例。匿名类可以“捕获”周围作用域中的变量。如果你对有状态类的定义是在其字段中携带可变内容的类(否则它几乎只是一个常量),那么你很幸运,因为这就是捕获的实现方式。这是一个小实验:

import java.lang.reflect.Field;
import java.util.function.Function;

public class Test {
    public static void main(String[] args) {
        final StringBuilder captured = new StringBuilder("foo");
        final String inlined = "bar";
        Function<String, String> lambda = x -> {
            captured.append(x);
            captured.append(inlined);

            return captured.toString();
        };

        for (Field field : lambda.getClass().getDeclaredFields())
            System.out.println(field);
    }
}

输出如下所示:

private final java.lang.StringBuilder Test$$Lambda$1/424058530.arg$1

引用变成了匿名 lambda 类的字段(并且为了提高效率,常量被内联,但这不是重点)。所以这个函数在大多数情况下应该做:StringBuilderfinal String inlined

public static boolean hasState(Function<?,?> lambda) {
    return lambda.getClass().getDeclaredFields().length > 0;
}

编辑 :正如@Federico所指出的那样,这是特定于实现的行为,可能不适用于某些外来环境或Oracle / OpenJDK JVM的未来版本。


答案 2

不,这通常是不可能的。检查 lambda 是否属于具有字段的类的建议方法是下一个最好的方法,但拥有字段并不等于具有状态。

class Stateless {
    int result = 0;
    public int getResult() { return result; }
}

可以通过找到两个输入序列来证明有状态性,给定的输入组合为其返回不同的结果。但是,不可能证明这样的输入序列不存在(如果前面附加另一个调用,任何输入序列都可能产生不同的结果)。

(即使您检查通过反射找到的字段的值,这些字段的值也可能在不影响 lambda 结果的情况下进行更改,因此不会真正使其具有状态)。

下面是一个简短的可编译示例,显示了假阳性和假阴性,反驳了这个概念:

public class StatefulLambda {
    static AtomicInteger counter = new AtomicInteger();

    public static void main(String[] args) {
        // false negative: will return different result each call
        System.out.println(hasState(i -> counter.incrementAndGet()));

        // false positive: will always return the same result
        Object object = new Object() {
            final int i = 0;
        };
        System.out.println(hasState(i -> object.toString()));
    }

    private static boolean hasState(Function<?,?> lambda) {
        return lambda.getClass().getDeclaredFields().length > 0;
    }
}

推荐