Scala中的'20秒'是如何工作的?

144
以下代码如何编译:
import scala.concurrent.duration._

val time = 20 seconds

这里实际上发生了什么?
2个回答

184

有几个要点需要注意。

首先,Scala允许在许多方法调用中省略点号和括号,因此20秒等同于20.seconds()*。

其次,应用了“隐式转换”。由于20是一个Int类型且Int没有seconds方法,编译器会搜索一个隐式转换,以取一个Int并返回一个具有seconds方法的对象,搜索的范围受限于您的方法调用作用域。

已经将DurationInt导入到您的作用域中。由于DurationInt是一个具有Int参数的隐式类,它的构造函数定义了一个Int => DurationInt的隐式转换。由于DurationInt具有seconds方法,因此它满足所有搜索条件。因此,编译器会将您的调用重写为new DurationInt(20).seconds**。

*这里用得不是很准确。实际上,20.seconds()无效,因为seconds方法没有参数列表,因此必须在方法调用中省略括号。

**实际上,这并不完全正确,因为DurationInt是一个值类,所以编译器会尽可能避免包装整数。


91
任何足够先进的技术都难以与魔法区分开来。 - ripper234
4
幸运的是,大多数IDE都能够区分它!Scala中经常使用隐式转换。如果你只是阅读文本文件,可能会感到困惑(“这个方法从哪里来”),但是通过适当的工具支持,你应该能够找到自己的方法,在这一点上Scala可以变得非常有意义和简洁。(例如,20秒比new DurationInt(20).seconds()更易读,只要你知道它是如何实现的) - William Billingsley
1
如果你确实需要使用隐式参数,一定要问自己是否有其他方法可以达到同样的效果而不需要它们的帮助。http://twitter.github.com/effectivescala/#Types and Generics-Implicits - oluies
5
实际上,seconds 方法是没有括号定义的,因此在使用括号调用它是错误的。 - Frank S. Thomas
2
@Frank,你说得很好。我并不是想表达在Scala中可以写20.seconds(),只是编译器会将调用翻译成这种方式。值得指出的是,如果相应的方法没有参数列表,Scala 要求你省略括号,就像这个例子一样。 - Aaron Novstrup

7
那里发生的“魔法”被称为“隐式转换”。你正在导入隐式转换,其中一些处理Int(和Double)与Duration之间的转换。这就是你要处理的东西。

3
你有没有想过为什么引入import scala.concurrent.duration._可以解析出20秒,但实际上导入DurationConversions特质却不行呢?编辑: 我刚意识到他们实际上导入的是 DurationInt。我猜这是因为你不能导入特质本身?只能导入特质的具体实现? - franklin

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接