4

As I know for inner and anonimous classes variables of outer scope is stored in generated bytecode (e.g. 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. Does it mean that for this 2 calls would be generated 2 separate methods ?

Ving
  • 55
  • 4
  • 2
    Have found an interesting reading on this topic: [Translation of lambda](http://cr.openjdk.java.net/~briangoetz/lambda/lambda-translation.html) – Ving May 06 '17 at 18:47
  • 2
    that not an *interesting* reading, that's *THE* reading about lambda translation... – Eugene May 06 '17 at 21:55

2 Answers2

7

This expression 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.

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:

 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. This is needed "carry" the elem variable.

Because of that every time map gets called a new instance is created.

Since your Stream has five initial elements, there will be 10 instances created - for the two map operations.

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.

Eugene
  • 117,005
  • 15
  • 201
  • 306
  • A bit of nitpicking: when you implement the raw `Function`, you’ll need an `Object apply(Object x)` method and type casts. This is what actually happens currently in the generated code. Further, `this.i` has to be `this.arg$1`. It is correct that the captured value will be the first parameter and the function parameter (`x`) the second. In the generated `lambda$curring$0` method, it would be less confusing to use `elem` and `x` as name for these parameters, rather than `x` and `y`… – Holger May 08 '17 at 10:11
  • @Holger that is by far not nitpicking in my world. It is quite amazing that this answer is actually hours of reading *your* (that's the main keyword in this comment) other answers and much debugging and lots of bytecode... thx for the advices. – Eugene May 08 '17 at 12:38
3

Everytime you call your curring(..) method, it will create a new object. So yes, you will have 2 objects.

This is because your lambda is not stateless, aka it is not working only on its own. It needs to capture an external variable, elem.

If a fixed number like 2 was used in your lambda rather than elem:

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

Your lambda would be stateless. It would not capture any external variable. In that case, your lambda would be a singleton called multiple times.

Notice that this behavior is JVM-dependant, and the above is from the HotSpot JVM.

More info here: Does a lambda expression create an object on the heap every time it's executed?

Community
  • 1
  • 1
kagmole
  • 2,005
  • 2
  • 12
  • 27