我正在尝试创建一个类型类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])
}
}
上述实现的行为与预期相同,但BigInt
和BigDecimal
(以及Numeric
的实例的其他用户定义类型)的情况除外null
而不是零。我应该怎么做才能使forNumeric
优先于forAnyRef
,我得到了我期望的行为?
答案 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
获得额外的特异性点,因为它对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)
可用的类型,此附加约束使forNumericVal
和forNumericRef
更具体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])
}
}