确定 lambda 表达式在 Java 中是无状态的还是有状态的
是否有一个函数接受对 lambda 表达式的引用并返回一个布尔值,说明 lambda 表达式是否为无状态?如何确定 lambda 表达式的有状态性?
是否有一个函数接受对 lambda 表达式的引用并返回一个布尔值,说明 lambda 表达式是否为无状态?如何确定 lambda 表达式的有状态性?
好吧,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 类的字段(并且为了提高效率,常量被内联,但这不是重点)。所以这个函数在大多数情况下应该做:StringBuilder
final String inlined
public static boolean hasState(Function<?,?> lambda) {
return lambda.getClass().getDeclaredFields().length > 0;
}
编辑 :正如@Federico所指出的那样,这是特定于实现的行为,可能不适用于某些外来环境或Oracle / OpenJDK JVM的未来版本。
不,这通常是不可能的。检查 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;
}
}