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)
^
Pingback引用通告: scala的诊断方法(4) -Ytyper-debug 编译项 | 在路上