我发现非常奇怪的行为(scala 2.9.1)使用和定义隐式值,并想知道是否有人可以解释它,或者它是否是scala bug?
我创建了一个自包含的示例:
object AnnoyingObjectForNoPurpose {
trait Printer[T] {
def doPrint(v: T): Unit
}
def print[T : Printer](v: T) = implicitly[Printer[T]].doPrint(v)
trait DelayedRunner extends DelayedInit {
def delayedInit(x: => Unit){ x }
}
// this works, as it should
object Normal extends DelayedRunner {
implicit val imp = new Printer[Int] {
def doPrint(v: Int) = println(v + " should work")
}
print(343)
}
// this compiles, but it shouldn't
// and won't run, cause the implicit is still null
object FalsePositive extends DelayedRunner {
print(123)
implicit val imp = new Printer[Int] {
def doPrint(v: Int) = println(v + " should not compile")
}
}
def main(args: Array[String]) {
implicit val imp = new Printer[Int] {
def doPrint(v: Int) = println(v + " should work")
}
print(44)
// print(33.0) // correctly doesn't work
Normal // force it to run
FalsePositive // force this to run too
}
}
答案 0 :(得分:2)
假设您将delayInit
的定义更改为无操作,即
def delayedInit(x: => Unit) { }
然后在你的main方法中执行类似
的操作println("FP.imp: " + FalsePositive.imp)
正如预期的那样,将打印FP.imp: null
,但练习的真正要点是说明定义FalsePositive
的主体的块的作用类似于常规类主体,而不是函数主体。它在看到val
时定义公共成员,而不是局部变量。
如果您将方法添加到AnnoyingObjectForNoPurpose
,如下所示,则无法编译,因为print
的隐含要求不满足。
def fails {
print(321)
implicit val cantSeeIt = new Printer[Int] {
def doPrint(v: Int) = println(v + " doesn't compile")
}
}
但是,如果您按照相同的原则定义了一个类,它将进行编译,但在初始化时会在运行时失败,就像您的FalsePositive
示例一样。
class Fine {
print(321)
implicit val willBeNull = new Printer[Int] {
def doPrint(v: Int) = println(v + " compiles, but fails")
}
}
要明确的是,Fine
的编译行为与implicit
的存在无关。类/对象初始化程序非常乐意使用引用未定义val
s的val
初始化程序进行编译。
object Boring {
val b = a
val a = 1
println("a=%s b=%s".format(a, b))
}
Boring
编译得很好,当它被引用时,会打印a=1 b=0
似乎你的问题可以归结为“是否应该将派生自DelayedInit
的类/对象的主体编译成类主体或功能块?”
看起来奥德斯基选择了前者,但你希望后者。
答案 1 :(得分:1)
这就像我写这个错误一样:
object Foo {
println(x)
val x = 5
}
这不是一个错误。你的构造函数向下对象体并按顺序发生。 DelayedInit
不是原因。这就是为什么在使用val / vars时要小心并确保它们首先初始化的原因。这也是人们使用lazy val来解决初始化顺序问题的原因。