在Java 8中,看起来类的lambda保存在数组中。例如,假设我们有这个类:
public class LambdaFactory {
public Supplier<Integer> getOne(){
return () -> 42;
}
public Supplier<Integer> getTwo(){
return () -> 128;
}
public Supplier<Integer> getThree(){
return () -> 3;
}
}
然后我这样打印出来:
System.out.println(factory.getOne());
System.out.println(factory.getOne());
System.out.println(factory.getTwo());
System.out.println(factory.getThree());
输出将类似于
examples.LambdaFactory$$Lambda$1@4e515669
examples.LambdaFactory$$Lambda$1@4e515669
examples.LambdaFactory$$Lambda$2@1b9e1916
examples.LambdaFactory$$Lambda$3@ba8a1dc
所以我们在这里可以看到两件事。两次调用的lambda给了我们相同的lambda对象(这与我们每次都可以得到一个新的内部类不同)。我们也看到它们看起来像是被保留在某种“Lambda”结构中,而这种结构是该类的一部分
我的问题是,我可以在课堂上看到lambdas吗?我没有任何理由这样做,我只是想解剖一些事情
答案 0 :(得分:10)
lambda是由JRE创建的,它们的创建方式由JRE控制,可能因不同的JRE供应商而异,并且可能在将来的版本中有所改变。
如果你想玩得开心,你可以在运行时创建一个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时发生的事情。但对于编译时(“实际”)lambda表达式,整个事物由单个invokedynamic
字节代码指令触发。 LambdaMetafactory.metafactory(…)
方法是 bootstrap 方法,在第一次执行invokedynamic
指令时调用该方法。返回的CallSite
对象与invokedynamic
指令永久关联。如果CallSite
是ConstantCallSite
并且其MethodHandle
在每次执行时都返回相同的lambda对象,则invokedynamic
指令将永久“生成”相同的lambda实例。
答案 1 :(得分:2)
The Java Language Specification states
在运行时,lambda表达式的评估类似于 在正常情况下评估类实例创建表达式 完成生成对象的引用。 [...]
具有以下属性的类的新实例是 已分配和初始化,或具有该类的现有实例 以下属性被引用。
[...]
这些规则旨在为实施提供灵活性 Java编程语言,在:
- 无需在每次评估时分配新对象。
- [...]
因此,在编译lambda表达式时,由编译器或运行时环境决定应该返回什么。
我的问题是,我可以在课堂上看到lambdas吗?我不 有任何理由这样做,我只是喜欢解剖事物
你可以认为一个lambda表达式作为任何其他类常量,String
,整数文字等。这些是出现在{{1的常量池中的常量文件。这些是对在运行时创建和存在的对象的引用。无法从类的常量池中引用实际对象。
在lambda的情况下,它无论如何都没有用,因为它实际上可能不是同一个对象。