我正在尝试为一个以 Squants 时间作为参数的类编写 Specs2 测试。诀窍在于,这两个工具都定义了一个隐式,它添加了一个名为“seconds”的方法来将数字转换为它们自己的表示形式(一种情况下是 sqants.time.Seconds,另一种情况下是 org.specs2.time.Duration),并且不幸的是,错误的似乎优先。
val a = new MyClass(10 seconds) // doesn't build because MyClass wants a Time instead of a Duration
明确地说,我可以通过不依赖隐式来构建 Squants Time 来解决这个问题,这不是问题。
我更喜欢隐式,所以我决定添加一个隐式来将持续时间转换为时间:
implicit def specsTimeToSquantsTime(t: org.specs2.time.Duration): squants.time.Time = squants.time.Seconds(t.toSeconds)
这让它进行了类型检查,但是当我运行测试时,我得到了堆栈溢出并且堆栈跟踪没有多大意义,它说我的隐式转换正在调用自身(它不会进行类型检查,即使鉴于上面的代码,它仍然是不可能的!):
[error] package.TestConversions$.specsTimeToSquantsTime(TestConversions.scala:9)
[error] package.TestConversions$.specsTimeToSquantsTime(TestConversions.scala:9)
[error] package.TestConversions$.specsTimeToSquantsTime(TestConversions.scala:9)
...
所以我有三个问题:这里发生了什么?有一个更好的方法吗?我可以手动隐藏 Int=>Duration 隐式吗?
最佳答案
方法toSeconds
定义在 Time
内类(class):
final class Time private (val value: Double) extends Quantity[Time] {
...
def toSeconds = to(Seconds)
...
}
https://github.com/garyKeorkunian/squants/blob/master/src/main/scala/squants/time/Time.scala
因此,当编译器看到在
Duration
上调用此方法时- 它正在寻找适当的隐式,它是 specsTimeToSquantsTime
,其中包含 Duration.toSeconds
反过来,这需要从 Duration
进行隐式转换至 Time
等等等等。因此,您在运行时会收到无限递归调用,这从编译器方面来说是完全正确的,因为理论上您可以停止这种递归,并且没有通用的方法(请参阅 halting problem )来检测它。有
NoTimeConversions
您可以在规范中混合使用特征以避免隐式转换:This trait can be used to deactivate the time conversions (to avoid conflicts with Akka's conversions for example
关于scala - 隐式转换导致无限递归,但这不应该进行类型检查,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27906905/