7

这在我的项目中是一个麻烦的违反类型安全的行为,所以我正在寻找一种方法来禁用它。似乎如果一个函数采用 AnyRef(或 java.lang.Object),您可以使用任何参数组合调用该函数,Scala 会将参数合并为一个 Tuple 对象并调用该函数。

在我的情况下,该函数不需要元组,并且在运行时失败。我希望这种情况会在编译时被捕获。

object WhyTuple {
 def main(args: Array[String]): Unit = {
  fooIt("foo", "bar")
 }
 def fooIt(o: AnyRef) {
  println(o.toString)
 }
}

输出:

(foo,bar)
4

6 回答 6

7

这里根本没有隐式或 Predef —— 只是很好的老式编译器魔法。您可以在类型检查器中找到它。我现在无法在规范中找到它。

如果你有足够的动力,你可以在编译器中添加一个 -X 选项来防止这种情况。

或者,您可以避免编写接受超类型的 arity-1 方法TupleN

于 2010-05-17T19:32:24.967 回答
4

像这样的东西怎么样:

object Qx2 {
    @deprecated def callingWithATupleProducesAWarning(a: Product) = 2
    def callingWithATupleProducesAWarning(a: Any) = 3
}

元组具有 Product 特征,因此任何传递元组的 callWithATupleProducesAWarning 调用都会产生弃用警告。

于 2010-05-17T18:32:05.533 回答
4

编辑:根据比我更了解的人,以下答案实际上是错误的:请参阅此答案。感谢Aaron Novstrup指出这一点。

这实际上是parser的一个怪癖,而不是类型系统或编译器的怪癖。Scala 允许在没有括号的情况下调用零参数或一参数函数,但不允许调用具有多个参数的函数。因此,正如Fred Haslam 所说,您所写的不是带有两个参数的调用,而是带有一个元组值参数的调用。但是,如果该方法确实采用了两个参数,则调用是一个双参数调用。似乎代码的含义会影响它的解析方式(这有点糟糕)。

至于您实际上可以对此做些什么,这很棘手。如果该方法确实需要两个参数,那么这个问题就会消失(即,如果有人错误地尝试用一个或三个参数调用它,他们会如您所愿地得到一个编译错误)。不要以为您一直推迟添加到该方法中的一些额外参数?:)

于 2010-05-17T17:21:05.413 回答
1

编译能够解释没有圆括号的方法。所以 fooIt 中的圆括号表示元组。您的调用与以下内容相同:

fooIt( ("foo","bar") )

话虽如此,如果您使用 Some(AnyRef) 或 Tuple1(AnyRef) 之类的包装器,您可以使该方法排除调用,并检索该值。

于 2010-05-17T16:37:26.070 回答
0

我认为 Predef 中 (x, y) 的定义是负责任的。“-Yno-predefs”编译器标志可能有一些用处,假设您愿意手动导入您需要的任何隐式。我的意思是你必须在所有地方添加 import scala.Predef._ 。

于 2010-05-17T17:00:55.420 回答
0

您是否还可以添加两个参数覆盖,这会阻止编译器应用语法糖?通过使类型适当模糊,您不太可能得到误报。例如:

object WhyTuple {

  ...

  class DummyType

  def fooIt(a: DummyType, b: DummyType) {
    throw new UnsupportedOperationException("Dummy function - should not be called")
  }
}
于 2010-05-18T09:03:37.027 回答