Scala相当于Java的Number

时间:2013-08-01 16:20:08

标签: scala

我正在尝试为数字域类型构建一个类型层次结构。例如YearIntNumber},PercentageDoubleNumber等等。{I}需要层次结构,以便我可以在值上调用toInttoDouble

但是,原始数字类型的Scala类型层次结构除了AnyVal之外没有共同的祖先。这不包含我需要的to{Int, Double}函数。

我能找到的最接近的类型是Numeric[T],它似乎主要存在于某些编译器技巧中。

在Java中,所有数字都来自Number(包括任意精度的数字)。 如何定义一个满足Scala中数字类型对象的接口?

我正在用鸭子打字来攻击它:

Any {
  def toInt: Int
  def toDouble: Double
}

不仅啰嗦,而且还会产生运行时反射成本。还有什么更好的吗?

2 个答案:

答案 0 :(得分:48)

Numeric[T]正是您所寻找的。 Scala的方法是类型类(即类似Numeric)。

而不是

def foo(x: java.lang.Number) = x.doubleValue

写一个

def foo[T](x: T)(implicit n: Numeric[T]) = n.toDouble(x)
def foo[T : Numeric](x: T) = implicitly[Numeric[T]].toDouble(x)

其中第二个(几乎)只是语法糖。

Numeric.Ops

当表达式更复杂时,每次需要操作时写入Numeric实例的调用都会变得笨拙。为了缓解这种情况,Numeric提供了隐式转换mkNumericOps,它使用编写数学运算的常用方法(即T而不是1 + 2)来扩充n.plus(1,2)。< / p>

要使用它们,只需导入隐式Numeric

的成员
def foo[T](x: T)(implicit n: Numeric[T]) = {
  import n._
  x.toDouble
}

请注意,由于import的限制,隐含的缩写语法在这里几乎不可取。

类型类

这里发生了什么?如果参数列表标记为implicit,则编译器将自动放置所需类型的值,如果该范围内标记为implicit的该类型的值只有一个。如果你写

foo(1.0)

编译器会自动将其更改为

foo(1.0)(Numeric.DoubleIsFractional)

为方法foo提供Double上的操作。

巨大的优势在于,您可以在不知道的情况下制作Numeric类型。假设您有一个为您提供类型MyBigInt的库。现在假设在Java世界中 - 不幸的是 - 开发人员没有扩展Number。你无能为力。

在Scala中,你可以写

implicit object MyBigIntIsNumeric extends Numeric[MyBigInt] {
   def compare(x: MyBigInt, y: MyBigInt) = ...
   // ...
}

您使用Numeric的所有代码现在都可以使用MyBigInt,但您无需更改库。所以Numeric甚至可能对您的项目是私有的,这种模式仍然有用。

答案 1 :(得分:-1)

来自@ gzm0的答案是一个静态解决方案,必须在编译时检查该类型,我给出了一个在运行时强制转换类型的动态解决方案,

def toDoubleDynamic(x: Any) = x match { case s: String => s.toDouble case jn: java.lang.Number => jn.doubleValue() case _ => throw new ClassCastException("cannot cast to double") }

使用大小写匹配在运行时选择正确的类型。