隐式参数解析 - 设置优先级

时间:2011-08-06 12:38:14

标签: scala implicit

我正在尝试创建一个类型类Default,它为给定类型提供默认值。这是我到目前为止所提出的:

trait Default[A] {
  def value: A
}

object Default {
  def withValue[A](a: A) = new Default[A] {
    def value = a
  }

  def default[A : Default]: A = implicitly[Default[A]].value

  implicit val forBoolean = withValue(false)

  implicit def forNumeric[A : Numeric] = 
    withValue(implicitly[Numeric[A]].zero)

  implicit val forChar = withValue(' ')

  implicit val forString = withValue("")

  implicit def forOption[A] = withValue(None : Option[A])

  implicit def forAnyRef[A >: Null] = withValue(null : A)
}

case class Person(name: String, age: Int)

case class Point(x: Double, y: Double)

object Point {
  implicit val pointDefault = Default withValue Point(0.0, 0.0)
}

object Main {
  def main(args: Array[String]): Unit = {
    import Default.default
    println(default[Int])
    println(default[BigDecimal])
    println(default[Option[String]])
    println(default[String])
    println(default[Person])
    println(default[Point])
  }
}

上述实现的行为与预期相同,但BigIntBigDecimal(以及Numeric的实例的其他用户定义类型)的情况除外null而不是零。我应该怎么做才能使forNumeric优先于forAnyRef,我得到了我期望的行为?

2 个答案:

答案 0 :(得分:13)

根据Scala引用的§6.26.3“重载分辨率”,选择forAnyRef隐式,因为它比<{1}} 更具体。有一种方法可以通过将其移动到forNumeric扩展的特征来降低其优先级,如下所示:

Default

但这只是技巧的一部分,因为现在trait LowerPriorityImplicits extends LowestPriorityImplicits { this: Default.type => implicit def forAnyRef[A >: Null] = withValue(null: A) } object Default extends LowerPriorityImplicits { // as before, without forAnyRef } forAnyRef都是彼此特定的,你会得到一个含糊不清的隐含错误。这是为什么?好吧,forNumeric获得额外的特异性点,因为它对forAnyRefA有一个非平凡的约束。那么,为了向A >: Null添加一个重要的约束,你可以做的是在forNumeric加倍:

Default

现在,对于implicit def forNumericVal[A <: AnyVal: Numeric] = withValue(implicitly[Numeric[A]].zero) implicit def forNumericRef[A <: AnyRef: Numeric] = withValue(implicitly[Numeric[A]].zero) 可用的类型,此附加约束使forNumericValforNumericRef更具体forAnyRef

答案 1 :(得分:6)

这是解决问题的另一种方法,不需要任何代码重复:

trait Default[A] {
  def value: A
}

object Default extends LowPriorityImplicits {
  def withValue[A](a: A) = new Default[A] {
    def value = a
  }

  def default[A : Default]: A = implicitly[Default[A]].value

  implicit val forBoolean = withValue(false)

  implicit def forNumeric[A : Numeric] = 
    withValue(implicitly[Numeric[A]].zero)

  implicit val forChar = withValue(' ')

  implicit val forString = withValue("")

  implicit def forOption[A] = withValue(None : Option[A])
}

trait LowPriorityImplicits { this: Default.type =>
  implicit def forAnyRef[A](implicit ev: Null <:< A) = withValue(null : A)
}

case class Person(name: String, age: Int)

case class Point(x: Double, y: Double)

object Point {
  implicit val pointDefault = Default withValue Point(0.0, 0.0)
}

object Main {
  import Default.default

  def main(args: Array[String]): Unit = {
    println(default[Int])
    println(default[BigDecimal])
    println(default[Option[String]])
    println(default[String])
    println(default[Person])
    println(default[Point])
  }
}