为什么Scala在解包元组时会构造一个新的元组?

Bla*_*jac 8 scala tuples iterable-unpacking

为什么这个Scala代码:

class Test
{
  def foo: (Int, String) =
  {
    (123, "123")
  }

  def bar: Unit =
  {
    val (i, s) = foo
  }
}
Run Code Online (Sandbox Code Playgroud)

生成以下字节码bar(),构造一个新的Tuple2,将Tuple2from 传递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
Run Code Online (Sandbox Code Playgroud)

这是因为编译器没有检查foo()s返回值不是元组吗?

JVM是否会优化构造?

huy*_*hjl 4

这似乎符合规范(在4.1 值声明和定义中- 为 stackoverflow 显示稍微重新格式化):

\n\n
\n

值定义也可以将模式 (\xc2\xa78.1) 作为左侧。如果 p 是除简单名称或名称后跟冒号和类型之外的某种模式,则值定义val p = e将扩展如下:

\n\n
    \n
  1. 如果模式p具有绑定变量x1, . . . , xn,其中n >= 1:\n 这里$x是一个新名称。
  2. \n
\n
\n\n
  val $x = e match {case p => (x1, . . . , xn)}\n  val x1 = $x._1\n  . . .\n  val xn = $x._n\n
Run Code Online (Sandbox Code Playgroud)\n\n

因此元组的创建发生在解析器阶段。因此val (i, s) = (1, "s")在解析器阶段结束时花费:

\n\n
private[this] val x$1 = scala.Tuple2(1, "s"): @scala.unchecked match {    \n  case scala.Tuple2((i @ _), (s @ _)) => scala.Tuple2(i, s)\n};\nval i = x$1._1;\nval s = x$1._2\n
Run Code Online (Sandbox Code Playgroud)\n\n

通过一百万次迭代的简单测试来测量这一点:

\n\n
def foo: (Int, String) = (123, "123")\ndef bar: Unit = { val (i, s) = foo }\ndef bam: Unit = { val f = foo; val i = f._1; val s = f._2 }\n
Run Code Online (Sandbox Code Playgroud)\n\n

产量

\n\n
foo: Elapsed: 0.030\nbar: Elapsed: 0.051\n._1 ._2 access: Elapsed: 0.040\n
Run Code Online (Sandbox Code Playgroud)\n\n

并带有 -optimize 标志:

\n\n
foo: Elapsed: 0.027\nbar: Elapsed: 0.049\n._1 ._2 access: Elapsed: 0.029\n
Run Code Online (Sandbox Code Playgroud)\n