问题描述
我正在尝试创建一个类型类Default
,该类型类提供给定类型的默认值.到目前为止,这是我想出的:
I am trying to create a typeclass Default
that supplies the default value for a given type. Here is what I have come up with so far:
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
并且得到我期望的行为?
The above implementation behaves as expected, except for the cases of BigInt
and BigDecimal
(and other user defined types that are instances of Numeric
) where it gives null
instead of zero. What should I do so that forNumeric
takes precedence over forAnyRef
and I get the behavior I expect?
推荐答案
之所以选择forAnyRef
隐式,是因为根据§6.26.3重载分辨率",它隐含的是比forNumeric
更具体 Scala参考.有一种方法可以通过将其移至Default
扩展的特征来降低其优先级,如下所示:
The forAnyRef
implicit is chosen because it is more specific than forNumeric
according to §6.26.3 "Overloading Resolution" of the Scala reference. There is a way to reduce its priority by moving it to a trait that Default
extends, like this:
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
中将其加倍:
But that's only part of the trick, because now both forAnyRef
and forNumeric
are as specific as each other, and you'll get an ambiguous-implicit error. Why is that? Well, forAnyRef
gets an extra specificity point because it has a non-trivial constraint on A
: A >: Null
. What you can do then, to add a nontrivial constraint to forNumeric
, is to double it in Default
:
implicit def forNumericVal[A <: AnyVal: Numeric] = withValue(implicitly[Numeric[A]].zero)
implicit def forNumericRef[A <: AnyRef: Numeric] = withValue(implicitly[Numeric[A]].zero)
现在,对于可用Numeric
的类型,此附加约束使forNumericVal
和forNumericRef
比forAnyRef
更具体.
Now, this additional constraint makes forNumericVal
and forNumericRef
more specific that forAnyRef
for types where a Numeric
is available.
这篇关于隐式参数解析-设置优先级的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!