简体   繁体   English

为lambda表达式存储out范围的变量

[英]Where variables of out scope is stored for lambda expressions

As I know for inner and anonimous classes variables of outer scope is stored in generated bytecode (eg OuterClass$1.class). 正如我所知,对于内部和无限类,外部作用域的变量存储在生成的字节码中(例如OuterClass $ 1.class)。 I would like to know where is stored variables of the next example: 我想知道下一个例子存储变量的位置:

public Function<Integer, Integer> curring(Integer elem) {
    return x -> x * elem;
}

Arrays.asList(1, 2, 3, 4, 5).stream().map(curring(2)).map(curring(5)).forEach(System.out::println);

Lambda is translated to methods, not classes. Lambda被转换为方法,而不是类。 Does it mean that for this 2 calls would be generated 2 separate methods ? 是否意味着对于这2个调用将生成2个单独的方法?

This expression x -> x * elem; 这个表达式x -> x * elem; is going to be de-sugared to a static method, that looks like this: 将被去除一个静态方法,看起来像这样:

  private static Integer lambda$curring$0(int x, int y){
      return x*y;
  }

Because you are capturing the elem variable inside the lambda, the lambda is said to be a stateful lambda . 因为你正在capturing lambda中的elem变量,所以lambda被认为是一个stateful lambda

On the other hand, your map operation needs an actual instance of java.util.Function - and that is generated at runtime that will kind of look like this: 另一方面,您的map操作需要一个java.util.Function的实际实例 - 并且在运行时生成它看起来像这样:

 final class Test2$$Lambda$1 implements java.util.function.Function {
      private final Integer arg$1;

      private Test2$$Lambda$1(Integer arg$1){
          this.arg$1 = arg$1;
      }

      // static factory method
      private static java.util.function.Function get$Lambda(Integer i){
            return new Test2$$Lambda$1(i); // instance of self
      }

       public Integer apply(Integer x) {
          return YourClass.lambda$curring$0(this.arg$1, x);    
       }
 }

Before Function.apply (inside your map operation) is called a new instance of Test2$$Lambda$1 is generated via the static factory method get$Lambda . 在调用Function.apply (在map操作中)之前,调用Test2$$Lambda$1新实例 。通过静态工厂方法get$Lambda生成Test2$$Lambda$1 This is needed "carry" the elem variable. 这需要“携带” elem变量。

Because of that every time map gets called a new instance is created . 因此, 每次调用map时都会创建一个新实例

Since your Stream has five initial elements, there will be 10 instances created - for the two map operations. 由于您的Stream有五个初始元素,因此将创建10个实例 - 用于两个map操作。

But generally this is an implementation detail that might easily change one day - so don't rely on it. 通常这是一个实施细节,有一天可能很容易改变 - 所以不要依赖它。 Also the creation and collection (by garbage collector) of this short lived instances are quite cheap and it will not impact your app almost at all. 此短期实例的创建和收集(通过垃圾收集器)也非常便宜,几乎根本不会影响您的应用程序。

Everytime you call your curring(..) method, it will create a new object. 每次调用curring(..)方法时,它都会创建一个新对象。 So yes, you will have 2 objects. 所以,是的,你将有2个对象。

This is because your lambda is not stateless, aka it is not working only on its own. 这是因为你的lambda不是无状态的,也就是说它不能单独工作。 It needs to capture an external variable, elem . 它需要捕获一个外部变量elem

If a fixed number like 2 was used in your lambda rather than elem : 如果你的lambda而不是elem使用了像2这样的固定数字:

public Function<Integer, Integer> curring() {
    return x -> x * 2;
}

Your lambda would be stateless. 你的lambda将是无国籍的。 It would not capture any external variable. 它不会捕获任何外部变量。 In that case, your lambda would be a singleton called multiple times. 在这种情况下,你的lambda将是一个多次调用的单例。

Notice that this behavior is JVM-dependant, and the above is from the HotSpot JVM. 请注意,此行为与JVM有关,而上述内容来自HotSpot JVM。

More info here: Does a lambda expression create an object on the heap every time it's executed? 更多信息: lambda表达式是否在每次执行时都在堆上创建一个对象?

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

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