Scala:为什么foo(1,2)和foo((1,2))的工作原理相同?

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),它是的语

假设我有一个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)
,它是的语法糖。所以
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
,也是一种很好的做法。