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

snr*_*snr 20 java lambda java-8 java-9

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

Arm*_*ius 12

Well, a lambda expression is just an instance of a special anonymous class that only has one method. Anonymous classes can "capture" variables that are in the surrounding scope. If your definition of a stateful class is one that carries mutable stuff in its fields (otherwise it's pretty much just a constant), then you're in luck, because that's how capture seems to be implemented. Here is a little experiment :

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);
    }
}
Run Code Online (Sandbox Code Playgroud)

The output looks something like this :

private final java.lang.StringBuilder Test$$Lambda$1/424058530.arg$1
Run Code Online (Sandbox Code Playgroud)

The StringBuilder reference got turned into a field of the anonymous lambda class (and the final String inlined constant was inlined for efficiency, but that's beside the point). So this function should do in most cases :

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

EDIT : as pointed out by @Federico this is implementation-specific behavior and might not work on some exotic environments or future versions of the Oracle / OpenJDK JVM.

  • 对不起,但我不相信拥有或不拥有字段是使方法实现(lambda)无状态或无状态的充分条件.我很确定`() - > ThreadLocalRandom.current().nextInt()`不能被视为'无状态',但你的测试不会显示任何捕获的字段. (6认同)
  • 如果你在非静态上下文中定义lambda,你也可以获得对封闭的`this`实例的引用......所有这些都是经过仔细和故意的无记录,并且不能以任何方式受到信任.明确地说,所有这些东西都是*特定于实现的*,这意味着没有任何有用的代码可以编写使用它的任何东西. (5认同)
  • @CoffeeNinja似乎在lambda表达式*可以*使用的内容和它*使用的内容之间存在一些混淆.由于您的代码仅评估实例*使用的内容,因此可以提及它没有注意到lambda表达式是否使用了诸如"ThreadLocalRandom"或"static"变量之类的东西.顺便说一句,它没有检测到它是否"在其字段中携带可变的东西",因为所有这些字段都是"final",你必须评估引用的对象,如果有的话,以找出它们是否是可变的. (3认同)
  • @FedericoPeraltaSchaffner你没有"得到"对封闭的`this`实例的引用.lambda表达式可以捕获封闭的`this`实例,如果它使用它,显式或使用其实例字段.不使用它的lambda表达式不捕获`this`.这已得到语言设计者的认可,但他们忘记在规范中明确说明. (2认同)

lex*_*ore 3

这是一个简单而愚蠢的想法。只需检查您的 lambda 是否有字段即可。

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

  List<Integer> serialStorage = new ArrayList<>();
  Function<? super Integer, ? extends Integer> statefulLambda =
      e -> { serialStorage.add(e); return e; };
Run Code Online (Sandbox Code Playgroud)

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

  statefulLambda.getClass().getDeclaredFields().length > 0
Run Code Online (Sandbox Code Playgroud)

可以用作 lambda 是有状态的指示器。

但是我不知道这通常是否有效。