Lambda 类文件在文件系统中的意义

Lambda 类文件在文件系统中的意义,lambda,java-8,method-reference,Lambda,Java 8,Method Reference,我正在研究lambda表达式,这本书举例说明了使用lambda表达式的toString()方法 Supplier<ArrayList<String>> s1 = ArrayList<String>::new; ArrayList<String> a1 = s1.get(); System.out.println(s1); //Output: functionalinterface.BuiltIns$$Lambda$1/791452441@1fb3e

我正在研究lambda表达式,这本书举例说明了使用lambda表达式的
toString()
方法

Supplier<ArrayList<String>> s1 = ArrayList<String>::new;
ArrayList<String> a1 = s1.get();
System.out.println(s1);
//Output: functionalinterface.BuiltIns$$Lambda$1/791452441@1fb3ebeb
供应商

创建类时,键入
.java
文件并将其编译为
.class
文件。这两个文件都是存在于计算机上某个位置的实际文件 文件系统。
$$
显示它是不同的。上没有
.java类
文件系统。相反,Java为我们创建了类。那样你 不必键入类,只需提供lambda即可 表情

作者回答说,这本书有点误导人。这句话确实正确,但稍加更正。它假设如果类名称中有
$
,则会自动创建(不是由您创建);这可能不是事实

例如,假设以下示例:

public class TestSO {

   public static void main(String[] args) {
      Test t = new Test() {
      };
   }

   static class Test {}
}
您已经创建了一个匿名内部类,它实际上是由编译器创建的普通类。如果编译
TestSO
,您将看到为您创建的名为
TestSO\$1.class
的类。如果您使用
javap-c-p TestSO\$1.class
检查它的外观,您将看到如下内容:

final class TestSO$1 extends TestSO$Test { ... 
invokedynamic #2,  0  // InvokeDynamic #0:get:()Ljava/util/function/Supplier;
但同时,声明包含
$
符号的类/方法是完全合法的:

static class $$Test2$$ {}
因此,
$
的存在并不能强烈地表明该类是由编译器/运行时生成的。另外,这是一个可能有一天会改变的实施细节

同时,这本书是正确的,因为
类不存在于文件系统的类文件中。它只存在于内存中

Supplier
是一个接口,您还没有提供实现它的类,是吗?接下来发生的事情很有趣

我不打算讲太多细节,但这里有一个简单的解释

如果您反编译您的示例(
javap-p-c-vtestso.class
),您将看到如下一行:

final class TestSO$1 extends TestSO$Test { ... 
invokedynamic #2,  0  // InvokeDynamic #0:get:()Ljava/util/function/Supplier;
invokedynamic
所做的是让
运行时
决定如何提供该
供应商的实际实例。而在运行时创建了一个实际的类,该类实现了所使用的
Supplier

您可以通过运行该类时使用的命令查看该类的外观:

-Djdk.internal.lambda.dumpProxyClasses=/Your/Path/Here
因此,在该路径中,您将看到一个名为:

TestSO$$Lambda$1.class 
同样,如果使用
javap-c-ptestso\$\$Lambda\$1.class对其进行反编译:

 final class TestSO$$Lambda$1 
         implements java.util.function.Supplier {....

作者想说的是,在运行时会为您生成一个实现供应商接口的
类,但不是您创建的

这篇引文试图用更简单或更通俗的术语来解释某些事情,避免过于正式,然而,结果可能被称为灾难性的

这个名字实际上毫无意义。为lambda表达式或方法引用提供的实例类及其名称。甚至还未确定执行
System.out.println(s1)时是否隐式调用了
toString()
方法将生成“classname@hashcode“产出


无论是否涉及文件系统,都是无关紧要的。正确且实际的一点是,在编译应用程序时,该实例的类不在Java编译器创建的类中。相反,它是由JRE在运行时以特定于实现的方式提供的。OpenJDK确实提供了一个生成的类,该类的名称恰好包含包含lambda表达式的类和一个
$$
,但正如前面所说,这是一个实现细节。而且,美元符号是Java标识符的合法部分,因此两个美元符号的存在并不能证明是一个特定的属性。

谢谢你,大师。它满足了我的期望,而不是一半。顺便说一句,我喜欢读你的答案。这就是为什么它被称为匿名:)