scala雾中风景(16): println(1,2,3)为什么work?

Console的println方法只有一个参数,但执行println(1,2,3)却可以打印出”(1,2,3)” 而不是报错:

scala> println(1,2,3)
(1,2,3)

编译器为什么会自作聪明的把里面的三个参数当成一个Tuple?

google了一下找到了这个issue, 在Typers.scala 里可以看到注释:

/** Try packing all arguments into a Tuple and apply `fun'
 *  to that. This is the last thing which is tried (after
 *  default arguments)
 */
def tryTupleApply: Option[Tree] = {
    // if 1 formal, 1 arg (a tuple), otherwise unmodified args
    val tupleArgs = actualArgs(tree.pos.makeTransparent, args, formals.length)
    ...
}

在编译的typer阶段,最后发现参数与方法定义要求的不匹配时,如果方法只有一个参数,编译器最后会尝试把所有参数打包为一个Tuple,把这个Tuple传递给方法。

这个特性之前没有在规范里明确定义出来。不过编译器有选项可以给出警告或报错的:

$ cat A.scala
object A {
    println(1,2,3)
}

$ scalac -Ywarn-adapted-args A.scala
A.scala:2: warning: Adapting argument list by creating a 3-tuple: this may not be what you want.
    signature: Predef.println(x: Any): Unit
given arguments: 1, 2, 3
after adaptation: Predef.println((1, 2, 3): (Int, Int, Int))
println(1,2,3)
        ^
one warning found

如果严格一些,可以使用-Yno-adapted-args选项禁止这个特性,这样编译时将报错:

$ scalac -Yno-adapted-args  A.scala
A.scala:2: error: too many arguments for method println: (x: Any)Unit
    println(1,2,3)
            ^
one error found

-Xlint里已经包含了-Ywarn-adapted-args,在编译时用-Xlint就可以发现这个警告了。

另外,auto-tupling的特性只能针对方法参数不超过一个情况,如果方法存在重载的且参数有超过2个的话将不起作用:

scala> def foo(p1:Any) { println(p1) }
foo: (p1: Any)Unit

scala> def foo(p1:Any, p2:Any) { println(p1.toString + p2.toString) }
foo: (p1: Any, p2: Any)Unit

scala> foo(1,2,3)
<console>:9: error: too many arguments for method foo: (p1: Any, p2: Any)Unit
          foo(1,2,3)
             ^

scala雾中风景(16): println(1,2,3)为什么work?》上有1条评论

  1. Pingback引用通告: scala的诊断方法(4) -Ytyper-debug 编译项 | 在路上

发表评论

电子邮件地址不会被公开。 必填项已用*标注