Java 获取lambdas类的列表

Java 获取lambdas类的列表,java,lambda,java-8,Java,Lambda,Java 8,在Java8中,类的lambda似乎保存在一个数组中。例如,假设我们有这个类: public class LambdaFactory { public Supplier<Integer> getOne(){ return () -> 42; } public Supplier<Integer> getTwo(){ return () -> 128; } public Supplier&

在Java8中,类的lambda似乎保存在一个数组中。例如,假设我们有这个类:

public class LambdaFactory {
    public Supplier<Integer> getOne(){
        return () -> 42;
    }

    public Supplier<Integer> getTwo(){
        return () -> 128;
    }

    public Supplier<Integer> getThree(){
         return () -> 3;
    }
}
输出将类似于

examples.LambdaFactory$$Lambda$1@4e515669
examples.LambdaFactory$$Lambda$1@4e515669
examples.LambdaFactory$$Lambda$2@1b9e1916
examples.LambdaFactory$$Lambda$3@ba8a1dc

我们可以看到两件事。同一个lambda调用了两次,给了我们相同的lambda对象(这与我们每次都可以得到一个新的内部类的anon类不同)。我们还看到它们看起来像是被保存在类的某个“Lambda”结构中


我的问题是,我能在课堂上找到兰姆达斯吗?我没有任何理由这么做,我只是喜欢剖析事物

lambda是由JRE创建的,它们的创建方式由JRE控制,在不同的JRE供应商之间可能会有所不同,在未来的版本中可能会发生变化

如果您想玩得开心,可以在运行时创建lambda,该lambda在类文件中没有相应的信息:

import java.lang.invoke.*;

public class ManualLambda {
  public static void main(String[] args) throws Throwable {
    MethodHandles.Lookup me=MethodHandles.lookup();
    MethodType t=MethodType.methodType(void.class);
    MethodType rt=MethodType.methodType(Runnable.class);
    CallSite site = LambdaMetafactory.metafactory(
      me, "run", rt, t, me.findStatic(ManualLambda.class, "sayHello", t), t);
    MethodHandle factory=site.getTarget();
    Runnable r=(Runnable)factory.invoke();

    System.out.println("created lambda: "+r);
    r.run();
  }
  private static void sayHello() {
      System.out.println("hello world");
  }
}
上面的代码追溯了创建lambda时发生的情况。但是对于编译时(“real”)lambda表达式,整个过程是由单个
invokedynamic
字节码指令触发的。
LambdaMetafactory.metafactory(…)
方法是在第一次执行
invokedynamic
指令时调用的引导方法。返回的
CallSite
对象与
invokedynamic
指令永久关联。如果
CallSite
是一个
ConstantCallSite
并且它的
MethodHandle
在每次执行时返回相同的lambda对象,那么
invokedynamic
指令将永远“生成”相同的lambda实例。

在运行时,lambda表达式的计算类似于 在正常情况下,对类实例创建表达式的求值 完成生成对对象的引用。[……]

具有以下属性的类的新实例 已分配并初始化,或具有 下面的属性被引用

[……]

这些规则旨在为的实现提供灵活性 Java编程语言,在该语言中:

  • 无需在每次评估时分配新对象。
  • [……]
因此,由编译器或运行时环境决定在计算lambda表达式时应返回什么

我的问题是,我能在课堂上找到兰姆达斯吗?我不 有什么理由这么做,我只是喜欢解剖东西

您可以将lambda表达式看作任何其他类常量、
字符串、整数文本等。这些常量出现在
.class
文件的常量池中。这些是对在运行时创建和存在的对象的引用。无法从类的常量池中引用实际对象


在lambda的情况下,它不会有任何帮助,因为它实际上可能不是同一个对象。

是什么让你认为它们在一个数组中?我认为你在这里得出了错误的结论。匿名内部类的名称中通常包含
$1
结构——通常在运行时构造带美元符号的类名,例如在使用动态
代理
类时。我想我不知道它是数组还是什么,但lambda是一样的。如果我在getOne的两个供应商上执行jUnit Assert.assertSame,它将通过,但如果它是annon内部类,它将失败。请参阅更新以删除我对数组的假设。我希望看到lambda“两次调用的同一个lambda给我们提供了相同的lambda对象”——不总是这样。如果lamba表达式引用外部作用域中的变量,则每次使用lamba时都会创建一个新实例。
import java.lang.invoke.*;

public class ManualLambda {
  public static void main(String[] args) throws Throwable {
    MethodHandles.Lookup me=MethodHandles.lookup();
    MethodType t=MethodType.methodType(void.class);
    MethodType rt=MethodType.methodType(Runnable.class);
    CallSite site = LambdaMetafactory.metafactory(
      me, "run", rt, t, me.findStatic(ManualLambda.class, "sayHello", t), t);
    MethodHandle factory=site.getTarget();
    Runnable r=(Runnable)factory.invoke();

    System.out.println("created lambda: "+r);
    r.run();
  }
  private static void sayHello() {
      System.out.println("hello world");
  }
}