javac生成的名称中的$$是什么意思?

What does $$ in javac generated name mean?

当通过 DependencyFinder 和 java-callgraph 等库生成的 java 调用图时,我发现 java 编译器为匿名函数生成名称,内部 classes等

我找到了其中几个的意思(如有错误请指正):

不过,我还发现:

  1. org.example.Bar$$Lambda.args
  2. org.example.Bar$$Lambda.call()
  3. org.example.Bar$$Lambda.lambdaFactory$()
  4. org.example.Bar$$Lambda.get$Lambda()

以上四个名字指的是什么?双元 ($$) 是什么意思?

lambda 表达式的 classes 不是 javac 生成的,而是由 JRE 在运行时创建的。它们的名称完全未指定,您不能依赖任何命名方案。

但显然,Oracle 当前的 JRE 具有可识别的模式。它将 $$Lambda$n 附加到定义 class 的名称,而 n 是一个递增的数字,它反映了运行时的 creation 命令,而不是任何属性 的编译代码。

您可以使用以下程序验证这一点:

public class Test {
    public static void main(String... args) {
        if(args.length==0) {
            final boolean meFirst = Math.random()<0.5;
            if(meFirst) {
                Runnable r=Test::main;
                System.out.println("first run:\t"+r.getClass());
            }
            main("second run");
            if(!meFirst) {
                Runnable r=Test::main;
                System.out.println("first run:\t"+r.getClass());
            }
        }
        else {
            Runnable r=Test::main;
            System.out.println(args[0]+":\t"+r.getClass());
            if(args[0].equals("second run")) main("last run");
        }
    }
}

根据随机meFirst标志的状态,它会打印

first run:  class Test$$Lambda
second run: class Test$$Lambda
last run:   class Test$$Lambda

second run: class Test$$Lambda
last run:   class Test$$Lambda
first run:  class Test$$Lambda

它表明第一个生成的 class 总是得到数字 1,无论它是第一个 main 调用中实例化的前两个方法引用之一还是第三个方法引用,在第一次递归中实例化。此外,第 3 次执行总是遇到与第 2 次相同的 class,因为它是相同的方法引用表达式(注意:distinct expression,因为所有表达式的目标都是相同的) 并且 class 被重新使用。

根据版本的不同,您可能会进一步看到类似 /number 的内容附加到名称中,这暗示名称实际上并不重要,因为每个 classes 都有另一个独特的标识符(它们被称为“匿名 classes”,您无法通过 ClassLoader 和名称找到它)。

这些 class 中的 args$n 等字段名称表示第 n 个捕获的值。由于 LambdaMetafactory 不知道捕获变量的实际名称,因此别无选择,只能生成此类名称。


但如前所述,这是一个实现工件。只要在每个定义class中为每个创建站点生成一个新的class,就可以保持这样的命名模式。但是由于规范允许任意 sharing/reusing 的 classes 和实例表示等效的 lambda 表达式(做同样的事情)和方法引用(针对相同的方法),这样的命名模式并不是每个实现策略都可能的.