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

时间:2018-02-20 14:42:28

标签: java lambda java-8 java-9

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

4 个答案:

答案 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是否为无状态的函数。

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