Scala 变量函数的编译目的是什么?

Scala 变量函数的编译目的是什么?,scala,variadic-functions,variadic,Scala,Variadic Functions,Variadic,在Java中,可变方法由编译器重新编写,以便它们成为采用可变参数所在数组的方法(如所示) 斯卡拉发生了什么 我主要关心的是,如果传递了另一种类型的集合,变量参数是否会隐式复制到数组,即编译器是否会以某种方式重新编写此代码段: val strings = Seq("hello") "%s".format(strings: _*) 以下几点 val strings = Seq("hello") "%s".format(strings.toArray: _*) 作为后续问题:变量方法实现Java接

在Java中,可变方法由编译器重新编写,以便它们成为采用可变参数所在数组的方法(如所示)

斯卡拉发生了什么

我主要关心的是,如果传递了另一种类型的集合,变量参数是否会隐式复制到
数组
,即编译器是否会以某种方式重新编写此代码段:

val strings = Seq("hello")
"%s".format(strings: _*)
以下几点

val strings = Seq("hello")
"%s".format(strings.toArray: _*)

作为后续问题:变量方法实现Java接口还是纯Scala有区别吗?

您可以使用
javap-v
非常轻松地检查这一点。如果使用2.12编译以下代码(现在使用
String.format
而不是
%s.format
):

class Example1 {
  val strings = Seq("foo")
  def formatResult = String.format("%s", strings: _*)
}
您将得到以下结果:

  public java.lang.String formatResult();
    descriptor: ()Ljava/lang/String;
    flags: ACC_PUBLIC
    Code:
      stack=4, locals=1, args_size=1
         0: ldc           #21                 // String %s
         2: aload_0
         3: invokevirtual #23                 // Method strings:()Lscala/collection/Seq;
         6: getstatic     #29                 // Field scala/reflect/ClassTag$.MODULE$:Lscala/reflect/ClassTag$;
         9: ldc           #31                 // class java/lang/String
        11: invokevirtual #35                 // Method scala/reflect/ClassTag$.apply:(Ljava/lang/Class;)Lscala/reflect/ClassTag;
        14: invokeinterface #41,  2           // InterfaceMethod scala/collection/Seq.toArray:(Lscala/reflect/ClassTag;)Ljava/lang/Object;
        19: checkcast     #43                 // class "[Ljava/lang/Object;"
        22: invokestatic  #47                 // Method java/lang/String.format:(Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/String;
        25: areturn
      LineNumberTable:
        line 3: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0      26     0  this   LExample1;
是的,它将
字符串
转换为数组

但是,如果您使用
“%s”。格式
,如下所示:

class Example2 {
  val strings = Seq("foo")
  def formatResult = "%s".format(strings: _*)
}
您将看不到转换:

  public java.lang.String formatResult();
    descriptor: ()Ljava/lang/String;
    flags: ACC_PUBLIC
    Code:
      stack=4, locals=1, args_size=1
         0: new           #21                 // class scala/collection/immutable/StringOps
         3: dup
         4: getstatic     #27                 // Field scala/Predef$.MODULE$:Lscala/Predef$;
         7: ldc           #29                 // String %s
         9: invokevirtual #33                 // Method scala/Predef$.augmentString:(Ljava/lang/String;)Ljava/lang/String;
        12: invokespecial #37                 // Method scala/collection/immutable/StringOps."<init>":(Ljava/lang/String;)V
        15: aload_0
        16: invokevirtual #39                 // Method strings:()Lscala/collection/Seq;
        19: invokevirtual #43                 // Method scala/collection/immutable/StringOps.format:(Lscala/collection/Seq;)Ljava/lang/String;
        22: areturn
      LineNumberTable:
        line 14: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0      23     0  this   LExample2;
请注意,这会生成两种编码,因此
bar(strings:*)
仍然不会涉及数组转换,因为在这种情况下,Scala编译器选择Scala编码的方法

总而言之:使用
seq:.*
从Scala调用Java varargs方法将始终涉及
toArray
seq
上被调用,而从Scala调用Scala varargs方法时不会发生这种情况(无论它们是否使用
@varargs
注释以实现Java兼容性)。

作为对(非常好)回答,请注意,在Scala 2.13中,varargs将使用
Scala.collection.immutable.Seq而不是
Scala.collection.Seq
。请参阅
class Example3 {
  def foo(xs: String*): Unit = ()

  @annotation.varargs
  def bar(xs: String*): Unit = ()

  val strings = Seq("foo")
  def fooResult = foo(strings: _*)
  def barResult = bar(strings: _*)
}