Scala 变量函数的编译目的是什么?
在Java中,可变方法由编译器重新编写,以便它们成为采用可变参数所在数组的方法(如所示) 斯卡拉发生了什么 我主要关心的是,如果传递了另一种类型的集合,变量参数是否会隐式复制到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接
数组
,即编译器是否会以某种方式重新编写此代码段:
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: _*)
}