为什么更喜欢隐式 val 而不是隐式对象

Lui*_*rez 4 design-patterns scala implicit

当询问有关隐式的问题时,与答案(或有时这就是答案本身)一起给出的常见建议/推荐/建议是implicit vals显式类型签名一起使用,而不是使用implicit objects

但是,这背后的原因是什么?

Lui*_*rez 6

“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 {}是行不通的。
(代码运行在这里完整的上下文这里

然而,可以说这些都是罕见的(高级)案例。因此,这可以被视为一个很好的指导方针。