Scala相当于Java的Number

fom*_*mil 26 scala

我正在尝试为数字域类型构建一个类型层次结构.例如Year是一个Int(这是一个Number),一个Percentage是一Double,这是一个Number等我需要的层次结构,这样我可以调用toInttoDouble上的值.

但是,原始数字类型的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.Ops

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甚至可以私有你的项目,这种模式仍然有效.