【问题标题】:Implicit parameter resolution - setting the precedence隐式参数解析 - 设置优先级
【发布时间】:2011-08-06 12:38:14
【问题描述】:

我正在尝试创建一个类型类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 并且我得到我期望的行为?

【问题讨论】:

    标签: scala implicit


    【解决方案1】:

    根据 Scala 参考的第 6.26.3 节“重载解决方案”,选择 forAnyRef 隐式是因为它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
    }
    

    但这只是技巧的一部分,因为现在forAnyRefforNumeric 彼此一样具体,你会得到一个模糊隐含的错误。这是为什么?好吧,forAnyRef 获得了一个额外的特异性点,因为它对A 有一个重要的约束: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)
    

    现在,对于Numeric 可用的类型,此附加约束使forNumericValforNumericRefforAnyRef 更具体。

    【讨论】:

    • 感谢吉恩的回答。调整您的解决方案我想出了另一个解决方案,可以避免这种小代码重复。为了读者的利益,我将其发布在下面。
    【解决方案2】:

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

    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])
      }
    }
    

    【讨论】:

      猜你喜欢
      • 2012-01-27
      • 2021-09-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-09-25
      • 2015-07-09
      • 1970-01-01
      相关资源
      最近更新 更多