我正在尝试创建一个类型类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])
}
}
Run Code Online (Sandbox Code Playgroud)
上述实现的行为与预期的一样,除了BigInt和BigDecimal(和其他用户定义的类型是其实例Numeric)的情况,它给出null而不是零.我该怎么办forNumeric才能优先考虑forAnyRef并且我得到了我期望的行为?
Jea*_*let 13
的forAnyRef隐式选择,因为它更具体的比forNumeric根据§6.26.3Scala的参考的"重载解决方案".有一种方法可以通过将其移动到Default扩展的特征来降低其优先级,如下所示:
trait LowerPriorityImplicits extends LowestPriorityImplicits {
this: Default.type =>
implicit def forAnyRef[A >: Null] = withValue(null: A)
}
object Default extends LowerPriorityImplicits {
// as before, without forAnyRef
}
Run Code Online (Sandbox Code Playgroud)
但是,这仅仅是技巧的一部分,因为现在双方forAnyRef并forNumeric有具体的对方,你会得到一个模棱两可的隐式错误.这是为什么?好吧,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)
Run Code Online (Sandbox Code Playgroud)
现在,这种附加的约束使得forNumericVal和forNumericRef更具体的是forAnyRef对类型,其中一个Numeric是可用的.
这是解决问题的另一种方法,不需要任何代码重复:
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])
}
}
Run Code Online (Sandbox Code Playgroud)