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

是否有一个函数接受对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 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

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

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

通过找到给定输入组合返回不同结果的两个输入序列,可以certificate有状态。 但是,不可能certificate这样的输入序列不存在(如果由另一个调用预先添加,则任何输入序列可能产生不同的结果)。

(即使你检查通过reflection找到的字段的值,这些可能会在不影响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; } } 

这是一个简单而愚蠢的想法。 检查你的lambda是否有字段。

例如,考虑以下有状态lambda。

  List serialStorage = new ArrayList<>(); Function statefulLambda = e -> { serialStorage.add(e); return e; }; 

这个statefulLambda serialStorage有一个私有的最终内部字段arg$1 ,它显然引用了serialStorage 。 所以

  statefulLambda.getClass().getDeclaredFields().length > 0 

可以用作lambda有状态的指标。

但是我不知道这一般是否有用。

我认为不可能编写一个可以确定lambda是否为无状态的函数:

查看Stream API的filter方法的示例, javadoc声明该参数必须是“无状态谓词”,并且还链接到API的无状态定义。

如果有一种方法可以确定filter (或任何其他)方法的参数是否为无状态,则Stream类将包含在参数为有状态lambda的情况下抛出IllegalArgumentException的可能性。 由于尚未实现并且仅向javadoc添加了警告,因此可以得出结论,无法编写可以确定lambda lambda是否为无状态的函数。


编辑 (在阅读Eric的评论之后):在很多情况下,实施团队做出实施选择以不实施特定function; 我们通常无法从这些选择中得出结论,其他人无法实现该function。 在这个特殊情况下,我认为如果有一个(计算上廉价的)解决方案,那么Java 8设计者就不会发现或完成它是不可信的。