我正在尝试为数字域类型构建一个类型层次结构。例如Year
是Int
(Number
},Percentage
是Double
,Number
等等。{I}需要层次结构,以便我可以在值上调用toInt
或toDouble
。
但是,原始数字类型的Scala类型层次结构除了AnyVal
之外没有共同的祖先。这不包含我需要的to{Int, Double}
函数。
我能找到的最接近的类型是Numeric[T]
,它似乎主要存在于某些编译器技巧中。
在Java中,所有数字都来自Number
(包括任意精度的数字)。 如何定义一个满足Scala中数字类型对象的接口?
我正在用鸭子打字来攻击它:
Any {
def toInt: Int
def toDouble: Double
}
不仅啰嗦,而且还会产生运行时反射成本。还有什么更好的吗?
答案 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
实例的调用都会变得笨拙。为了缓解这种情况,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")
}
使用大小写匹配在运行时选择正确的类型。