如果我理解正确,在JVM下,每次使用lambda表达式时,都必须创建一个Object。
为什么要开销?为什么Scala创建者在设计FunctionN类型时选择扩展AnyRef而不是AnyVal?我的意思是,它们本身没有任何真正的“值”,所以函数不应该是具有底层单位表示的值对象(或者包含一些哈希用于相等性检查或其他什么)?我可以想象,不为每个lambda分配一个对象可以在某些代码库中提高性能。
我想到的扩展AnyVal的一个明显缺点是它会禁止子类化函数类型。也许仅仅这一点就不足以扩展AnyVal,但还有其他原因吗?
- 编辑
我理解函数需要关闭其他变量,但我认为将它建模为apply方法的参数更自然,而不是FunctionN对象的字段成员(从而消除了拥有java.lang的必要性)。这部分的对象) - 毕竟,不是在编译时所有已知的变量都被关闭了吗?
- 再次修改
我发现了它;我想到的是'lambda lifting'。
答案 0 :(得分:6)
调用方法的唯一方法是字节码操作invokevirtual
(类上的虚拟调度),invokeinterface
(相同,但在接口上),invokespecial
(完全调用 给定方法,忽略虚拟查找。用于private
,super
和new
。)和invokestatic
(召唤独角兽调用static
方法)。 invokespecial
已经出局了,因为调用一些函数是抽象函数的对立面。 invokestatic
也已出局,因为它本质上是invokespecial
克隆,不需要this
参数。 invokevirtual
和invokeinterface
足够相似,我们的目的被视为相同。
没有办法像你在C中看到的那样传递一个普通的函数指针,即使你可以,也永远不能调用它,因为没有指令可以跳转到代码中的任意一点。 (方法中的所有跳转目标都限制在该方法中,并且对外部的所有引用都归结为字符串。(当然,JVM在加载文件后可以自由地优化该表示。)
为了使用任一指令调用方法,JVM必须在目标对象的虚拟调度表中查找方法。如果你试图用()
虚拟出对象(AnyVal
子类直到2.10才存在,但让我们怀疑我们的怀疑),那么JVM会让你感到非常困惑试图将一种(可能有趣的)方法称为接近"无特征的blob"你可以得到。
还要记住,对象的方法完全取决于它的类。如果a.getClass == b.getClass
,则a
和b
具有完全相同的方法,代码和所有内容。为了解决这个问题,我们需要创建FunctionN
特征的子类,这样每个子类代表一个函数,每个类的每个实例都包含对其类的引用,该类包含对与之关联的代码的引用。那个功能。即使使用invokedynamic
,这仍然是正确的,因为LambdaMetaFactory
的当前实现在运行时创建了内部类。
最后,函数不需要状态的假设是错误的,正如@Oleg指出的那样。闭包需要保持对其环境的引用,这只能通过对象实现。