为什么使用方法引用会在字节码中生成内部 class?

Why does using a method reference generate an inner class in byte code?

我写了一个使用方法参考的简单例子:

public class Main {
private static String identity(String param) {
    return param;
}

public static void main(String... args) {
    Function<String, String> fun = Main::identity;
    System.out.println(fun.apply("Hello"));
}}

生成的字节码是InnerClass:

InnerClasses:
 public static final #68= #67 of #71; //Lookup=class java/lang/invoke/MethodHandles$Lookup of class java/lang/invoke/MethodHandles
BootstrapMethods:
0: #35 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:
  #36 (Ljava/lang/Object;)Ljava/lang/Object;....

我以为这个 innerClass 是用在 lambda bootstrap 方法中,但我不知道 jvm 什么时候创建这个 class 的对象以及这个 [=22= 的对象中会存储什么信息].谁能给我解释一下?

这不是生成的内部 class,而是 JVMS §4.7.6 中指定的 InnerClasses 属性。规范要求此属性列出每个 class(在常量池中引用)不是包的成员。

基本上,javac 为它在代码中看到的所有内部 class 生成 InnerClasses 属性。例如

public class Test {
    public static void main(String[] args) {
        for (Map.Entry e : Collections.emptyMap().entrySet()) {

        }
    }
}

上面 class 的字节码将引用 Map.Entry 作为 Map 的内部 class:

InnerClasses:
   public static #31= #7 of #23; //Entry=class java/util/Map$Entry of class java/util/Map

在 JDK 中,lambda 是用 invokedynamic 指令实现的。根据规范,invokedynamic 的解析涉及使用以下类型的 4 个参数调用 bootstrap 方法:

  1. MethodHandle
  2. MethodHandles.Lookup
  3. String
  4. MethodType

Java 运行时在内部创建这些对象,但由于字节码引用 MethodHandles.Lookup class,javac 为其生成 InnerClasses 属性。