3

Now I can understand that compiler why emit o.getClass() to checking whether the reference is null,since compiler make sure enclosing class instance is not null before pass it into inner class constructor then inner class can refer enclosing class instance by EnclosingClass.this when create inner class / lambda expression instance.

class EnclosingClass {
    class InnerClass {
        public Object reference() {
            return this;//this refer to InnerClass instance
        }
    }

    Supplier<Object> reference = () -> {
        return this;//why this refer to EnclosingClass instance???
    };
}

Thanks to @Holger give me more feedback & references, After dump byte code by using javap -v -p -c com.holi.functions.EnclosingClass command, I found invokedynamic instruction before put a field of Supplier via putfield.

 4: aload_0
 5: aload_0
 6: invokedynamic #2,  0              // InvokeDynamic #0:get:(Lcom/holi/functions/EnclosingClass;)Ljava/util/function/Supplier;
11: putfield      #3                  // Field reference:Ljava/util/function/Supplier;

and invokedynamic instruction invoked with a BootstrapMethod 0:

BootstrapMethods:
  0: #26 invokestatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
    Method arguments:
      #27 ()Ljava/lang/Object;
      #28 invokespecial com/holi/functions/EnclosingClass.lambda$new$0:()Lcom/holi/functions/EnclosingClass;
      #29 ()Lcom/holi/functions/EnclosingClass;

the BootstrapMethods 0 refered a function implemetation method lambda$new$0 in EnclosingClass that generated by compiler.

private com.holi.functions.EnclosingClass lambda$new$0();
Code:
     0: aload_0
     1: areturn

Question

A synthetic lambda expression inner class created by JVM in memory (it is an inner class but not generated with class file so I think it is generated in memory) via MethodHandle.invoke.

@Test
void lambdaClassCreatedAConstructorWithAParameterOfLambdaContextClass() throws Throwable {
    Object[] parameterTypes = Arrays.stream(lambdaClass.getDeclaredConstructors())
            .map(Constructor::getParameterTypes).toArray(Object[]::new);

    assertThat(parameterTypes, equalTo(new Object[][]{{lambdaContextClass}}));
}

@Test
void lambdaClassCreatedAnInstanceFieldOfLambdaContextClass() throws Throwable {
    Object[] fieldTypes = Arrays.stream(lambdaClass.getDeclaredFields())
            .map(Field::getType).toArray(Object[]::new);

    assertThat(fieldTypes, equalTo(new Object[]{lambdaContextClass}));
}

invokedynamic doing something in constructor like this:

private Supplier<EnclosingClassLambdaExpressionTest> invokeDynamic() throws Throwable {
    MethodHandles.Lookup caller = MethodHandles.lookup();
    MethodType instantiatedMethodType = methodType(lambdaContextClass);
    // InvokeDynamic #0:get:(Lcom/holi/functions/EnclosingClass;)Ljava/util/function/Supplier;
    // BootstrapMethods:
    //   0: #23 invokestatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
    //     Method arguments:
    //       #24 ()Ljava/lang/Object;
    //       #25 invokespecial com/holi/functions/EnclosingClass.lambda$new$0:()Lcom/holi/functions/EnclosingClass;
    //       #26 ()Lcom/holi/functions/EnclosingClass;
    CallSite site = LambdaMetafactory.metafactory(caller,
            "get"
            , methodType(Supplier.class, lambdaContextClass)
            , methodType(Object.class)
            , caller.findVirtual(lambdaContextClass, "lambda$new$0", instantiatedMethodType)
            , instantiatedMethodType
    );
    return (Supplier<EnclosingClassLambdaExpressionTest>) site.dynamicInvoker().invokeExact(lambdaContext);
}

So this in lambda expression is an instance of EnclosingClass, since the lambda expression implementation method defined on EnclosingClass. and the lambda expression inner class is adapter class to adaptee implementation method of EnclosingClass(lambda$new$0) to FunctionInterface(Supplier). What I said above right? Please give me more references link if I'm right/wrong,thanks everybody.

Community
  • 1
  • 1
holi-java
  • 29,655
  • 7
  • 72
  • 83
  • is the question "why was it designed that way" or "how does referencing `this` in a lambda work" ? – Joni Mar 30 '17 at 16:26
  • @Joni I want to see why and how referer `this` to an instance of encloing class in lambda expression. – holi-java Mar 30 '17 at 16:35
  • 2
    Don’t think of a lambda expression as a synthetic anonymous class. That anonymous class is just a helper to implement the functional interface. A lambda expression denotes a *function* which lives in the same context, where it has been declared. The closest pre-lambda construct is a method within the same class (hence, `this`, `super` and `getClass()` have the same meaning as outside and `private` members can be accessed without synthetic helper methods). Plus the possibility to capture local variables from the surrounding context, but that’s the only thing it shares with inner classes. – Holger Mar 30 '17 at 16:39
  • @Holger first, thanks. could you give me more details or some external references? – holi-java Mar 30 '17 at 16:45
  • 1
    The linked question has an answer with a link to the JLS. Besides that, the topic “lambda expression” will bring up millions of articles, e.g. in Google. Most of them will tell you that lambda expression describe *anonymous functions*. It is not bad to read some non-Java specific articles about it, as it helps to understand, that the synthetic anonymous class is just a Java specific implementation detail to make the much broader concept fit into Java’s object oriented type system. – Holger Mar 30 '17 at 16:54
  • @Holger thanks so much.I'm not good at english, so I need more detailed articles. – holi-java Mar 30 '17 at 17:00
  • @Holger I might have been found the answer, please help me look that I described is right? – holi-java Mar 31 '17 at 08:50
  • Too much details, but anyway, the `invokedynamic` instruction isn’t doing what you have written in `mock`; as you can see, this information is already there in the bootstrap method arguments, as the work has been done by the compiler. Only the last line can be seen to be roughly equivalent, though it will be more like `(R)site.dynamicInvoker().invokeExact(context)`. But as said, these are too much unnecessary details, the only thing that matters, is, that the lambda expression’s code has been compiled to a synthetic method in the declaring class, which happens to fit the *semantic* quite well. – Holger Mar 31 '17 at 08:57
  • @Holger Thanks again. I just want to know jvm how to solve surrounding context in lambda expression, so I described more detailed. That is, what I said above is right? – holi-java Mar 31 '17 at 09:03

0 Answers0