为什么这个Scala代码:
class Test
{
def foo: (Int, String) =
{
(123, "123")
}
def bar: Unit =
{
val (i, s) = foo
}
}
为构建新bar()
的{{1}}生成以下字节码,将Tuple2
从Tuple2
传递给它,然后从中获取值?
foo()
这是因为编译器没有检查public void bar();
Code:
0: aload_0
1: invokevirtual #28; //Method foo:()Lscala/Tuple2;
4: astore_2
5: aload_2
6: ifnull 40
9: new #7; //class scala/Tuple2
12: dup
13: aload_2
14: invokevirtual #32; //Method scala/Tuple2._1:()Ljava/lang/Object;
17: aload_2
18: invokevirtual #35; //Method scala/Tuple2._2:()Ljava/lang/Object;
21: invokespecial #20; //Method scala/Tuple2."<init>":(Ljava/lang/Object;Ljava/lang/Object;)V
24: astore_1
25: aload_1
26: invokevirtual #39; //Method scala/Tuple2._1$mcI$sp:()I
29: istore_3
30: aload_1
31: invokevirtual #35; //Method scala/Tuple2._2:()Ljava/lang/Object;
34: checkcast #41; //class java/lang/String
37: astore 4
的返回值不是元组吗?
JVM是否会优化构造?
答案 0 :(得分:4)
这似乎是scalac错失的优化机会。
编译器的相关部分我们Unapplies#caseClassUnapplyReturnValue,调用TreeDSL#SOME
生成代码以创建新的TupleN
答案 1 :(得分:4)
这似乎是根据spec( 4.1值声明和定义 - 稍微重新格式化stackoverflow显示):
值定义也可以有一个模式(第8.1节)作为左侧。如果p是除简单名称或名称后跟冒号和类型之外的某种模式,则值定义
val p = e
扩展如下:
- 如果模式
醇>p
已绑定变量x1, . . . , xn
,则其中n >= 1
: 在这里,$x
是一个新名称。
val $x = e match {case p => (x1, . . . , xn)}
val x1 = $x._1
. . .
val xn = $x._n
因此,元组创建发生在解析器阶段。所以val (i, s) = (1, "s")
在解析器阶段结束时花费:
private[this] val x$1 = scala.Tuple2(1, "s"): @scala.unchecked match {
case scala.Tuple2((i @ _), (s @ _)) => scala.Tuple2(i, s)
};
val i = x$1._1;
val s = x$1._2
在这个关于百万次迭代的简单测试中进行测量:
def foo: (Int, String) = (123, "123")
def bar: Unit = { val (i, s) = foo }
def bam: Unit = { val f = foo; val i = f._1; val s = f._2 }
产量
foo: Elapsed: 0.030
bar: Elapsed: 0.051
._1 ._2 access: Elapsed: 0.040
并使用-optimize标志:
foo: Elapsed: 0.027
bar: Elapsed: 0.049
._1 ._2 access: Elapsed: 0.029