考虑以下代码:
object DelayedClassTagInference {
trait SS {
type TT <: Any
implicit val ctg: ClassTag[TT] = implicitly[ClassTag[TT]]
val fakeCtg: ClassTag[None.type] = implicitly[ClassTag[None.type]]
}
class Sub1 extends SS {
override final type TT = Int
}
class Sub2 extends SS {
override final type TT = Double
}
class Sub3 extends SS {
override final type TT = String
}
}
class DelayedClassTagInference extends FunSpec {
import DelayedClassTagInference._
it("") {
val sub1 = new Sub1()
println(sub1.fakeCtg)
println(sub1.ctg)
}
}
在初始化Sub1和Sub2时,已经确定了TT类型,因此可以使用类型类规则轻松推断出ClassTag [Int]和ClassTag [Double]。
不幸的是,当我运行上面的代码时。我得到以下结果:
scala.None$
null
因此ctg的值为null,除了触发NullPointerException外,这也没有意义。是斯卡拉袋子,以后应该修理吗?
答案 0 :(得分:3)
删除implicit
的修饰符val ctg
,您将看到代码未编译。您不应该手动定义隐式ClassTag
/ TypeTag
/ WeakTypeTag
,它们应该在类型已知时由编译器自动生成。
实际上,当您调用implicitly[ClassTag[TT]]
所定义的隐式val ctg: ClassTag[TT]
时,会使用它,这就是运行时null
的原因。
隐式在编译时解决,当您调用sub1.ctg
时,解析哪个.ctg
被调用发生在运行时(这就是子类型多态的工作原理)。在编译时,尚不知道它是Sub1#ctg
。
替换
implicit val ctg: ClassTag[TT] = implicitly[ClassTag[TT]]
与
def ctg(implicit tag: ClassTag[TT]): ClassTag[TT] = implicitly[ClassTag[TT]]
,您将在运行时拥有Int
,而不是null
。