scala - 执行捕获变量的嵌套函数分配
问题描述
scala 中的嵌套函数可以捕获父函数中的变量。
例如
def outer = {
var a = 0
def inner = {
a = 42
}
inner()
a
}
在 C# 中,这是通过将所有捕获的变量存储在一个结构中并将该结构传递给 byref 来实现的。这避免了嵌套函数分配,除非您将其转换为函数对象。请参阅Sharplab 中的此示例。
然而,在 scala 中,您不能通过 ref 传递变量,因此唯一可行的方法是将所有捕获的变量存储在一个对象上,然后将该对象传入。
这是否意味着如果嵌套函数在 scala 中捕获任何变量,它的每次调用都会分配?
解决方案
变量a
本身仍然在outer
方法的堆栈帧中,而它所引用的对象是在堆上分配的,就像所有 Java 对象一样(即使a
应该表示原始类型)。
通过javap -v
在你的代码上运行,我们可以看到它a
实际上是一个类型的最终变量scala.runtime.IntRef
,它包含一个可以更新的整数字段。嵌套方法inner
变成了一个静态方法,它接受一个类型的参数IntRef
并将其elem
字段设置为 42。这有点类似于 C# 方法,但为每个变量创建一个对象而不是一个结构来保存所有变量。
public int outer();
descriptor: ()I
flags: (0x0001) ACC_PUBLIC
Code:
stack=1, locals=2, args_size=1
0: iconst_0
1: invokestatic #16 // Method scala/runtime/IntRef.create:
(I)Lscala/runtime/IntRef;
4: astore_1
5: aload_1
6: invokestatic #20 // Method inner$1:(Lscala/runtime/IntRef;)V
9: aload_1
10: getfield #24 // Field scala/runtime/IntRef.elem:I
13: ireturn
String
编辑:让我们这次尝试一下:
class ClosureTest {
def outer = {
var a = ""
def inner() = {
a = "42"
}
inner()
a
}
}
输出javap
:
public java.lang.String outer();
descriptor: ()Ljava/lang/String;
flags: (0x0001) ACC_PUBLIC
Code:
stack=1, locals=2, args_size=1
0: ldc #12 // String
2: invokestatic #18 // Method scala/runtime/ObjectRef.create:(Ljava/lang/Object;)Lscala/runtime/ObjectRef;
5: astore_1
6: aload_1
7: invokestatic #22 // Method inner$1: (Lscala/runtime/ObjectRef;)V
10: aload_1
11: getfield #26 // Field
scala/runtime/ObjectRef.elem:Ljava/lang/Object;
14: checkcast #28 // class java/lang/String
17: areturn
这一次,因为String
不是原始类型,所以使用了类ObjectRef
(它有一个表示包装值的类型参数),但它基本上还是一样的。尽管 JVM 不允许您ref
像 C# 那样拥有参数,但对象仍然通过引用传递,因此a
仍然可以修改所持有的对象/原语的值。
这是我能找到的唯一文档的链接。还有许多其他类,例如BooleanRef
, FloatRef
,以及它们的 volatile 对应类,例如VolatileDoubleRef
,VolatileObjectRef
等。这些类中的每一个基本上只有一个可变公共字段,编译器在需要捕获变量的“真实”值时使用该字段。
推荐阅读
- php - 在 WooCommerce 变量产品下拉列表中隐藏特定产品属性术语
- asp.net-mvc - 未从现有应用程序中的新 MVC 视图创建 Umbraco 模板
- scala - 如何覆盖特定类型的 Scala 泛型方法?
- html - 如何使用 VBA 一次抓取多个页面/链接?
- matlab - 警告“X 在机器精度范围内的等级不足”问题
- javascript - Recaptcha 2.0 错误:reCAPTCHA 占位符元素必须是元素或 id
- html - 组件旁边添加了额外的像素(Airbnb 移动应用程序)?
- python - 当 top = False 时,ResNet50 keras 给出错误的形状输出
- javascript - 如何以简单的形式避免 NaN 错误?
- godot - 基于 Godot 3.2 中另一个节点的镜像 3D 节点位置