我正在尝试为数字域类型构建一个类型层次结构.例如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 次 |
最近记录: |