隐式参数解析——设置优先级
我正在尝试创建一个类型类 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
并且获得我期望的行为?
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])
}
}
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?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
选择
forAnyRef
隐式是因为根据 Scala 参考的第 §6.26.3“重载解析”,它比forNumeric
更具体。有一种方法可以通过将其移动到Default
扩展的特征来降低其优先级,如下所示:但这只是技巧的一部分,因为现在
forAnyRef
和>forNumeric
彼此一样具体,您将收到一个不明确的隐式错误。这是为什么?嗯,forAnyRef
获得了一个额外的特异性点,因为它对A
有一个重要的约束:A >: Null
。然后,您可以做的就是向forNumeric
添加一个重要的约束,将其在Default
中加倍:现在,这个附加约束使
forNumericVal
和对于可用Numeric
的类型,forNumericRef
比forAnyRef
更具体。The
forAnyRef
implicit is chosen because it is more specific thanforNumeric
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 thatDefault
extends, like this:But that's only part of the trick, because now both
forAnyRef
andforNumeric
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 onA
:A >: Null
. What you can do then, to add a nontrivial constraint toforNumeric
, is to double it inDefault
:Now, this additional constraint makes
forNumericVal
andforNumericRef
more specific thatforAnyRef
for types where aNumeric
is available.这是解决问题的另一种方法,不需要任何重复代码:
Here is another way to solve the problem, doesn't require any code duplication: