Lui*_*rez 4 design-patterns scala implicit
当询问有关隐式的问题时,与答案(或有时这就是答案本身)一起给出的常见建议/推荐/建议是implicit vals与显式类型签名一起使用,而不是使用implicit objects。
但是,这背后的原因是什么?
“TL;博士;” 原因是implici val具有显式类型签名的 animplicit object具有您想要的确切类型,而 an具有不同的类型。
说明为什么这可能是一个问题的最好方法是用一个简单的例子:
// 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
Run Code Online (Sandbox Code Playgroud)
请记住:
如果有多个符合条件的参数与隐式参数的类型匹配,则将使用静态重载解析规则选择最具体的参数。
那么,答案是:"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
Run Code Online (Sandbox Code Playgroud)
在这种情况下,我们将有一个“模糊的隐含值”异常;因为这两个选项同样具体。
不。
与软件工程中的大多数事情一样,没有什么是通用的。
在某些情况下,使用implicit val具有显式类型签名的 an 是不可能的 (例如,因为即使编译器知道该类型在源代码中也不可写),或者它不会产生正确的结果而 animplicit object会。
一个简单的例子是:
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
Run Code Online (Sandbox Code Playgroud)
这样你就可以要求implicitly[A.Aux[b.T]]而使用implicit val b: B = new B {}是行不通的。
(代码运行在这里完整的上下文这里)。
然而,可以说这些都是罕见的(高级)案例。因此,这可以被视为一个很好的指导方针。