30

我正在尝试为数字域类型构建类型层次结构。例如 aYear是 an Int(这是 a Number), aPercentage是 a Double,这是 aNumber等。我需要层次结构,以便我可以调用toInttoDouble在值上。

但是,基本数字类型的 Scala 类型层次结构除了AnyVal. 这不包含to{Int, Double}我需要的功能。

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

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

我目前正在用鸭子打字来破解它:

Any {
  def toInt: Int
  def toDouble: Double
}

这不仅冗长,而且会产生运行时反射成本。有更好的吗?

4

2 回答 2

60

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当表达式更复杂时,每次需要操作时编写对实例的调用可能会变得笨拙。为了减轻这种情况,Numeric提供了隐式转换mkNumericOps,它增加T了编写数学运算的常用方法(即1 + 2,而不是n.plus(1,2))。

为了使用这些,只需导入implicit 的成员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他们不知道的情况下制作类型。假设您有一个为您提供 type 的库MyBigInt。现在假设在 Java 世界中——不幸的是——开发人员没有扩展它Number。你无能为力。

在 Scala 中,你可以只写

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

并且您使用的所有代码Numeric现在都可以使用,MyBigInt您不必更改库。所以Numeric甚至可以是您的项目私有的,并且这种模式仍然有效。

于 2013-08-01T16:37:47.050 回答
-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") }

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

于 2016-04-22T11:03:23.323 回答