javac生成的$$中的$$是什么意思?
当转移由DependencyFinder和java-callgraph等库生成的java调用图时,我发现java编译器为匿名函数,内部类等生成名称。
我已经发现了其中几个的含义(如果我错了,请更正):
-
org.example.Bar$Foo
引用Foo
,它是org.example.Bar
的内部类。 -
org.example.Bar$1
引用在org.example.Bar$1
一个方法中声明的匿名类。 -
org.example.Bar.lambda$spam$1()
是指在org.example.Bar.spam()
方法中声明的lambda。
但是,我还发现:
-
org.example.Bar$$Lambda$2.args$1
-
org.example.Bar$$Lambda$2.call()
-
org.example.Bar$$Lambda$7.lambdaFactory$()
-
org.example.Bar$$Lambda$7.get$Lambda()
上面的四个名字是指什么? 双美元( $$
)是什么意思?
lambda表达式的类不是javac
生成的,而是由JRE在运行时创建的。 他们的名字完全没有说明,你不能依赖任何命名方案。
但显然,Oracle目前的JRE具有可识别的模式。 它将$$Lambda$n
附加到定义类的名称,而n
是递增的数字,它反映了运行时的创建顺序,而不是编译代码的任何属性。
您可以使用以下程序validation:
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$1 second run: class Test$$Lambda$2 last run: class Test$$Lambda$2
要么
second run: class Test$$Lambda$1 last run: class Test$$Lambda$1 first run: class Test$$Lambda$2
它表明第一个生成的类总是得到数字1
,无论它是第一个main
调用中实例化的前两个方法引用之一,还是第一个在第一个递归中实例化的第三个方法引用。 此外,第3次执行总是遇到与第2次相同的类,因为它是相同的方法引用表达式(注意:distinct expression ,因为所有表达式的目标是相同的)并且类被重用。
根据版本的不同,您可能会在名称后面看到/number
附加的内容,这些内容暗示名称确实无关紧要,因为每个类都有另一个唯一标识符(它们可以称为“匿名类”,您可以使用找不到ClassLoader
和name。
这些类中的args$n
等字段名称代表第n个捕获值。 由于LambdaMetafactory
不知道捕获变量的实际名称,因此除了生成这样的名称之外别无选择。
但正如所说,这是一个实现工件。 只要为每个定义类中的每个创建站点生成一个新类,就可以维护这样的命名模式。 但由于规范允许任意共享/重用表示等效lambda表达式(执行相同操作)和方法引用(针对相同方法)的类和实例,因此每种实现策略都无法实现这样的命名模式。