简体   繁体   English

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

[英]Determine if a lambda expression is stateless or stateful in Java

Is there a function which accepts a reference to a lambda expression and returns a boolean saying whether the lambda expression is stateless or not? 是否有一个函数接受对lambda表达式的引用并返回一个布尔值,表示lambda表达式是否为无状态? How can the statefulness of a lambda expression be determined? 如何确定lambda表达式的有状态?

Well, a lambda expression is just an instance of a special anonymous class that only has one method. 嗯,lambda表达式只是一个只有一个方法的特殊匿名类的实例。 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);
    }
}

The output looks something like this : 输出看起来像这样:

private final java.lang.StringBuilder Test$$Lambda$1/424058530.arg$1

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). StringBuilder引用变成了匿名lambda类的一个字段(并且final String inlined常量被内联以提高效率,但这不是重点)。 So this function should do in most cases : 所以这个功能在大多数情况下都应该做:

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

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 . 编辑:正如@Federico所指出的,这是特定于实现的行为,并且可能无法在某些特殊环境或未来版本的Oracle / OpenJDK JVM上运行

No, it is not generally possible. 不,通常不可能。 The suggested approach of checking whether the lambda belongs to a class with a field is the next best thing, but having a field does not equal having a state. 检查lambda是否属于具有字段的类的建议方法是下一个最好的方法,但是具有字段不等于具有状态。

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

It is possible to prove statefulness by finding two input sequence for which a given input combination returns a different result. 通过找到给定输入组合返回不同结果的两个输入序列,可以证明有状态。 However, it is not possible to prove that such a input sequence does not exist (any input sequence might produce a different result if prepended by another invocation). 但是,不可能证明这样的输入序列不存在(如果由另一个调用预先添加,则任何输入序列可能产生不同的结果)。

(Even if you check the values of fields found via reflection, those might change without influencing the lambda's result, therefore not really making it stateful). (即使您检查通过反射找到的字段的值,这些可能会在不影响lambda结果的情况下发生变化,因此不会使其成为有状态)。

Here's a short compilable example showing both false positive and negatives, disproving the notion: 这是一个简短的可编辑示例,显示了误报和否定,反驳了这个概念:

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;
    }
}

Here's a simple and stupid idea. 这是一个简单而愚蠢的想法。 Just check if your lambda has fields. 检查你的lambda是否有字段。

For instance, consider the following stateful lambda. 例如,考虑以下有状态lambda。

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

This statefulLambda has a private final internal field arg$1 which obviously references serialStorage . 这个statefulLambda serialStorage有一个私有的最终内部字段arg$1 ,它显然引用了serialStorage So 所以

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

could be used as indicator that lambda is stateful. 可以用作lambda有状态的指标。

However I have no idea if this will generally work. 但是我不知道这一般是否有用。

I would argue that it is not possible to write a function that can determine if a lambda is stateless or not: 我认为不可能编写一个可以确定lambda是否为无状态的函数:

Looking for example at the filter method of the Stream API, the javadoc states that the parameter must be "a [...] stateless predicate" and also links to the API's definition of stateless . 例如,在Stream API的filter方法中, javadoc声明该参数必须是“无状态谓词”,并且还链接到API的无状态定义。

If there was a way to determine if the parameter of the filter (or any other) method was stateless or not, the Stream class would have included the possibility to throw an IllegalArgumentException in case the parameter was a stateful lambda. 如果有一种方法可以确定filter (或任何其他)方法的参数是否为无状态,则Stream类可能会在参数为有状态lambda的情况下抛出IllegalArgumentException As this has not been implemented and only a warning was added to the javadocs, one can conclude that there is no way write a function that can determine if a lambda lambda is stateless. 由于尚未实现并且仅向javadoc添加了警告,因此可以得出结论,无法编写可以确定lambda lambda是否为无状态的函数。


Edit (after reading the comments from Eric): There are plenty of situations where an implementation team makes implementation choices to not implement a particular feature; 编辑 (阅读Eric的评论之后):在很多情况下,实施团队会做出实施选择而不实施特定功能; we usually cannot conclude from those choices that the feature is impossible for someone else to implement. 我们通常无法从这些选择中得出结论,其他人无法实现该功能。 In this special case, I believe its implausible that the Java 8 designers would not have found or done it if there was a (computational cheap) solution. 在这种特殊情况下,我认为如果有一个(计算上廉价的)解决方案,那么Java 8设计者就不会发现或完成它是不可信的。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM