是否有一个函数接受对lambda表达式的引用并返回一个布尔值,说明lambda表达式是否为无状态?如何确定lambda表达式的有状态?
答案 0 :(得分:12)
嗯,lambda表达式只是一个只有一个方法的特殊匿名类的实例。匿名类可以" 捕获"周围范围内的变量。如果你对有状态类的定义是在其字段中携带可变东西的那个(否则它只是一个常数),那么你很幸运,因为那是how capture seems to be implemented 。这是一个小实验:
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
StringBuilder
引用变成了匿名lambda类的字段(并且final String inlined
常量被内联以提高效率,但除了这一点之外)。所以这个功能在大多数情况下都应该这样做:
public static boolean hasState(Function<?,?> lambda) {
return lambda.getClass().getDeclaredFields().length > 0;
}
编辑:正如@Federico所指出的,这是特定于实现的行为,可能不适用于某些特殊环境或 Oracle / OpenJDK JVM 的未来版本。
答案 1 :(得分:3)
不,一般不可能。检查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;
}
}
答案 2 :(得分:2)
这是一个简单而愚蠢的想法。检查你的lambda是否有字段。
例如,考虑以下有状态的lambda。
List<Integer> serialStorage = new ArrayList<>();
Function<? super Integer, ? extends Integer> statefulLambda =
e -> { serialStorage.add(e); return e; };
此statefulLambda
有一个私人最终内部字段arg$1
,显然会引用serialStorage
。所以
statefulLambda.getClass().getDeclaredFields().length > 0
可以用作lambda有状态的指示符。
但我不知道这一般是否有效。
答案 3 :(得分:1)
我认为不可能编写一个能够确定lambda是否为无状态的函数:
例如,在Stream API的filter
方法中,javadoc表明参数必须是“a [...]无状态谓词”以及链接到API的stateless。
如果有办法确定filter
(或任何其他)方法的参数是否为无状态,则Stream类可能会抛出IllegalArgumentException以防万一参数是一个有状态的lambda。由于尚未实现并且仅向javadoc添加了警告,因此可以得出结论,无法编写可以确定lambda lambda是否为无状态的函数。