Scala:为什么foo(1,2)和foo((1,2))的工作原理相同?
假设我有一个Scala函数:Scala:为什么foo(1,2)和foo((1,2))的工作原理相同?,scala,tuples,Scala,Tuples,假设我有一个Scala函数: def func(x:(Int,Int)):Int = x._1 + x._2 func((1,2)) // This works as expected 但是下面的函数调用怎么也能正常工作呢 func(1,2) 我知道函数调用会通过apply方法转换为object,但我看不出这是如何工作的?如果没有合适的多参数方法和一个合适的单参数方法,Scala编译器将尝试将这些逗号分隔的参数转换为元组 func方法的参数x的类型是(Int,Int),它是的语
def func(x:(Int,Int)):Int = x._1 + x._2
func((1,2)) // This works as expected
但是下面的函数调用怎么也能正常工作呢
func(1,2)
我知道函数调用会通过apply方法转换为object,但我看不出这是如何工作的?如果没有合适的多参数方法和一个合适的单参数方法,Scala编译器将尝试将这些逗号分隔的参数转换为元组
func
方法的参数x
的类型是(Int,Int)
,它是的语法糖。所以func
方法的签名实际上是func(Tuple2[Int,Int])
您以func(1,2)
的形式调用它,但在范围中没有定义签名为func(Int,Int)
的方法,因此编译器将粗略地将调用转换为与方法签名匹配的func(Tuple2(1,2))
。因此,这种调用可以工作,但可能会导致意外的结果(不难看出原因)
编辑:另请参阅问题以获得更多阅读。这是scala的语法:
scala> def bar(x: Int, y: Int) = func(x, y)
scala> :javap -c bar
Compiled from "<console>"
public class $line5.$read$$iw$$iw$ {
public static $line5.$read$$iw$$iw$ MODULE$;
public static {};
Code:
0: new #2 // class $line5/$read$$iw$$iw$
3: invokespecial #23 // Method "<init>":()V
6: return
public int bar(int, int);
Code:
0: getstatic #30 // Field $line3/$read$$iw$$iw$.MODULE$:L$line3/$read$$iw$$iw$;
3: new #32 // class scala/Tuple2$mcII$sp
6: dup
7: iload_1
8: iload_2
9: invokespecial #35 // Method scala/Tuple2$mcII$sp."<init>":(II)V
12: invokevirtual #39 // Method $line3/$read$$iw$$iw$.func:(Lscala/Tuple2;)I
15: ireturn
public $line5.$read$$iw$$iw$();
Code:
0: aload_0
1: invokespecial #42 // Method java/lang/Object."<init>":()V
4: aload_0
5: putstatic #44 // Field MODULE$:L$line5/$read$$iw$$iw$;
8: return
}
(x_1,…,x_n),((x_1,…,x_n))
是元组$n$($x_1,…,x_n$)的简写形式
看看这个
并且在检查生成的字节码时:
scala> def bar(x: Int, y: Int) = func(x, y)
scala> :javap -c bar
Compiled from "<console>"
public class $line5.$read$$iw$$iw$ {
public static $line5.$read$$iw$$iw$ MODULE$;
public static {};
Code:
0: new #2 // class $line5/$read$$iw$$iw$
3: invokespecial #23 // Method "<init>":()V
6: return
public int bar(int, int);
Code:
0: getstatic #30 // Field $line3/$read$$iw$$iw$.MODULE$:L$line3/$read$$iw$$iw$;
3: new #32 // class scala/Tuple2$mcII$sp
6: dup
7: iload_1
8: iload_2
9: invokespecial #35 // Method scala/Tuple2$mcII$sp."<init>":(II)V
12: invokevirtual #39 // Method $line3/$read$$iw$$iw$.func:(Lscala/Tuple2;)I
15: ireturn
public $line5.$read$$iw$$iw$();
Code:
0: aload_0
1: invokespecial #42 // Method java/lang/Object."<init>":()V
4: aload_0
5: putstatic #44 // Field MODULE$:L$line5/$read$$iw$$iw$;
8: return
}
有什么必要这样做?有什么具体的原因来实现这个额外的检查吗?它不是一个额外的检查,只是一个编译器黑客,你可以通过生成的字节码来验证。我无法向您指出确切的来源,但我相信不建议使用这种用法。它可能是很久以前放弃统一参数列表和元组的努力的产物。在Scala 2.11和2.12中,编译器警告:“通过创建2元组来调整参数列表:这可能不是您想要的”。您在问题中没有提到这一点,这让我怀疑您没有看到或没有注意到警告,这不是一个好的做法。@SethTisue我在2.11.8和2.12.1上都进行了验证(有和没有
-deprecation
),编译器在这种情况下没有显示任何警告。虽然它确实显示了警告:通过插入()对参数列表进行自适应已被弃用:这不可能是您想要的。
当我执行列表(1,2,3).toSet()
。抱歉,我忘了启用了-Xlint
,这确实会显示警告。即使在REPL中启用-Xlint
,也是一种很好的做法。