我正在尝试为数字域类型构建一个类型层次结构.例如Year是一个Int(这是一个Number),一个Percentage是一Double,这是一个Number等我需要的层次结构,这样我可以调用toInt或toDouble上的值.
但是,原始数字类型的Scala类型层次结构除了之外没有共同的祖先AnyVal.这不包含to{Int, Double}我需要的功能.
我能找到的最接近的类型是Numeric[T],它似乎主要存在于某些编译器技巧中.
在Java中,所有数字都来自Number(包括任意精度的数字).如何定义一个满足Scala中数值类型对象的接口?
我正在用鸭子打字来攻击它:
Any {
def toInt: Int
def toDouble: Double
}
Run Code Online (Sandbox Code Playgroud)
这不仅啰嗦,而且还会产生运行时反射成本.有更好的吗?
gzm*_*zm0 50
Numeric[T]正是你要找的.Scala的方法是类型类(即类似的东西Numeric).
代替
def foo(x: java.lang.Number) = x.doubleValue
Run Code Online (Sandbox Code Playgroud)
写一个
def foo[T](x: T)(implicit n: Numeric[T]) = n.toDouble(x)
def foo[T : Numeric](x: T) = implicitly[Numeric[T]].toDouble(x)
Run Code Online (Sandbox Code Playgroud)
其中第二个(几乎)只是语法糖.
Numeric当表达式更复杂时,每次需要操作时写入对实例的调用都会变得笨拙.为了缓解这种情况,Numeric提供了隐式转换mkNumericOps,它T通过编写数学运算的常用方式(即1 + 2而不是n.plus(1,2))来增强.
为了使用它们,只需导入隐式的成员Numeric:
def foo[T](x: T)(implicit n: Numeric[T]) = {
import n._
x.toDouble
}
Run Code Online (Sandbox Code Playgroud)
请注意,由于对import隐含的缩写语法的限制,这里几乎不可取.
这里发生了什么?如果参数列表被标记为implicit,则编译器将自动将所需类型的值放在那里,如果implicit该范围内标记为存在的那个类型的恰好值.如果你写
foo(1.0)
Run Code Online (Sandbox Code Playgroud)
编译器会自动将其更改为
foo(1.0)(Numeric.DoubleIsFractional)
Run Code Online (Sandbox Code Playgroud)
为该方法foo提供操作Double.
这样做的巨大优势在于,您可以在Numeric不知情的情况下制作类型.假设您有一个为您提供类型的库MyBigInt.现在假设在Java世界中 - 不幸的是 - 开发人员没有扩展它Number.你无能为力.
在Scala中,你可以写
implicit object MyBigIntIsNumeric extends Numeric[MyBigInt] {
def compare(x: MyBigInt, y: MyBigInt) = ...
// ...
}
Run Code Online (Sandbox Code Playgroud)
您使用的所有代码Numeric现在都可以使用,MyBigInt但您不必更改库.所以Numeric甚至可以私有你的项目,这种模式仍然有效.
| 归档时间: |
|
| 查看次数: |
10987 次 |
| 最近记录: |