2

第一个是正确的 Scala 代码但第二个甚至无法编译的可能性有多大?

编译的那个

object First {
  class ABC(body: => Unit) {
    val a = 1
    val b = 2
    println(body)
  }

  def main(args: Array[String]): Unit = {
    val x = new ABC {
      a + b
    }
  }
}

这个不能在 Scala 2.11 和 2.12 上编译

object Second {
  class ABC(body: => Int) {
    val a = 1
    val b = 2
    println(body)
  }

  def main(args: Array[String]): Unit = {
    val x = new ABC {
      a + b
    }
  }
}
4

2 回答 2

2

这一点都不奇怪。让我们看第一个例子:

您声明您的类ABC以接收一个按名称传递的参数,该参数返回Unit并且您认为这个片段:

   val x = new ABC {
      a + b
    }

正在传递该body参数,它不是。真正发生的是:

val x = new ABC(()) { a + b }

如果您运行该代码,您将看到println(body) prints ()因为您没有为body参数传递值,编译器允许它编译,因为正如 scaladoc 所述,只有 1 个 type 值Unit

Unit 是 scala.AnyVal 的子类型。Unit 类型的值只有一个,(),并且它不被底层运行时系统中的任何对象表示。具有返回类型 Unit 的方法类似于声明为 void 的 Java 方法。

由于只有一个值,编译器允许您省略它,它将填补空白。单例对象不会发生这种情况,因为它们不扩展AnyVal。Just has the default value for Intis0的默认值Unit()因为只有这个值可用,所以编译器接受它。

文档

如果 ee 具有某个值类型并且预期类型是 Unit,则通过将 ee 嵌入到术语 { ee; 中将其转换为预期类型。() }。

单例对象不会扩展AnyVal,因此它们不会得到相同的对待。

当您使用如下语法时:

new ABC {
 // Here comes code that gets executed after the constructor code. 
 // Code here can returns Unit by default because a constructor always 
 // returns the type it is constructing. 
}

您只是在构造函数主体中添加内容,而不是传递参数。

第二个示例无法编译,因为编译器无法推断默认值,body: => Int因此您必须显式传递它。

结论

构造函数的括号内的代码与传递参数不同。在相同的情况下它可能看起来相同,但这是由于“魔法”。

于 2017-08-28T14:18:16.633 回答
0

您不能将单个参数传递给花括号中的构造函数,因为这将被解析为定义匿名类。如果你想这样做,你还需要将大括号括在普通大括号中,如下所示:

new ABC({
  a + b
})

至于为什么编译器会接受new ABC {a + b},解释有点错综复杂,出乎意料:

  1. new ABC {...}相当于new ABC() {...}
  2. new ABC()可以被解析为new ABC(())因为自动元组,这是规范中没有提到的解析器的一个特性,见SI-3583 规范没有提到自动元组。相同的功能导致以下代码编译没有错误:

    def f(a: Unit) = {}
    f()
    
    def g(a: (Int, Int)) = {}
    g(0,1)
    

    请注意,该调用会产生警告(即使您的原始示例也会产生警告):

    通过插入 () 来适应参数列表已被弃用:这不太可能是您想要的。

    从 2.11 开始产生警告,请参阅问题SI-8035 Deprecate automatic () injection

于 2017-08-29T13:22:41.750 回答