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

时间:2017-05-06 08:11:40

标签: java memory lambda java-8

正如我所知,对于内部和无限类,外部作用域的变量存储在生成的字节码中(例如OuterClass $ 1.class)。 我想知道下一个例子存储变量的位置:

padding='SAME'

Lambda被翻译为方法,而不是类。是否意味着对于这2个调用将生成2个单独的方法?

2 个答案:

答案 0 :(得分:7)

这个表达式x -> x * elem;将去除静态方法,如下所示:

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

因为lambda中的capturing变量是elem,所以lambda被认为是stateful lambda

另一方面,您的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);    
       }
 }

在调用Function.apply之前(在地图操作中),通过静态工厂方法Test2$$Lambda$1生成get$Lambda新实例。这是必要的"携带" elem变量。

因为每次调用地图时都会创建一个新实例

由于您的Stream有五个初始元素,因此将为这两个map操作创建10个实例。

一般这是一个可能很容易在某一天改变的实施细节 - 所以不要依赖它。此短期实例的创建和收集(通过垃圾收集器)非常便宜,几乎不会影响您的应用程序。

答案 1 :(得分:3)

每次调用curring(..)方法时,都会创建一个新对象。所以,是的,你将有2个对象。

这是因为你的lambda不是无状态的,也就是说它不能单独使用它。它需要捕获外部变量elem

如果您的lambda中使用了2这样的固定数字而不是elem

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

你的lambda将是无国籍的。它不会捕获任何外部变量。在这种情况下,你的lambda将是一个多次调用的单例。

请注意,此行为与JVM有关,而上述内容来自HotSpot JVM。

此处有更多信息:Does a lambda expression create an object on the heap every time it's executed?