"TL;DR;" 原因是具有显式类型签名的 implici val 具有您想要的确切类型,而 implicit object 具有不同的类型。
说明问题原因的最佳方法是举一个简单的例子:
// Having the following definitions.
trait SomeTrait[T] {
def f: T
}
trait ExtendedTrait[T] extends SomeTrait[T] {
def g: T
}
implicit val SomeStringVal: SomeTrait[String] = new SomeTrait[String] {
override def f: String = "from val f"
}
implicit val ExtendedStringVal: ExtendedTrait[String] = new ExtendedTrait[String] {
override def f: String = "from extended val f"
override def g: String = "from extended val g"
}
implicit object ExtendedStringObject extends ExtendedTrait[String] {
override def f: String = "from extended obj f"
override def g: String = "from extended obj g"
}
// What will be the result of:
implicitly[SomeTrait[String]].f
记住:
如果有多个符合条件的参数与隐式参数的类型匹配,则将使用静态重载决策规则选择最具体的参数。
那么,答案是:"from extended obj f"。
上面的(有点意外)的结果,是因为对象有自己的类型(单例类型ExtendedStringObject.type是ExtendedTrait[String]的子类型)比其他人更“具体”。
现在,这可能是一个问题的原因是大多数人不知道object 有自己的类型,而且它比他们认为的更具体。尽管如此,这也可能使object 在您不希望的时候被选中;例如:
// If instead we only had this:
implicit val ExtendedStringVal: ExtendedTrait[String] = new ExtendedTrait[String] {
override def f: String = "from extended val f"
override def g: String = "from extended val g"
}
implicit object ExtendedStringObject extends SomeTrait[String] {
override def f: String = "from extended obj f"
}
// Then what will be the result of:
implicitly[SomeTrait[String]].f
在这种情况下,我们会有一个“模糊隐含值”异常;因为这两个选项同样特定。
规则是否通用?
没有。
与软件工程中的大多数事情一样,没有什么是通用的。
在某些情况下,使用带有显式类型签名的implicit val 是not possible (例如,因为即使编译器知道它存在,该类型在源代码中也不可写) 或@987654322 @.
一个简单的例子是:
trait A {
type X
}
object A {
type Aux[XX] = A { type X = XX }
}
class B extends A {
type X = T
class T
}
implicit object b extends B
implicit object b1 extends B
这样您就可以请求implicitly[A.Aux[b.T]],而使用implicit val b: B = new B {} 将不起作用。
(运行here 完整上下文here 的代码)。
但是,可以说这些是罕见的(高级)案例。因此,这可以被认为是一个很好的指导方针。