我正在尝试为数字域类型构建类型层次结构。例如一个 Year
是 Int
(这是一个 Number
),一个 Percentage
是 Double
,这是一个 Number
等。我需要层次结构,以便我可以调用 toInt
或 toDouble
关于值(value)观。
但是,基本数字类型的 Scala 类型层次结构除了 AnyVal
之外没有共同祖先。 .这不包含 to{Int, Double}
我需要的功能。
我能找到的最接近的类型是 Numeric[T]
,这似乎主要是为了一些编译器的诡计而存在的。
在 Java 中,所有来自 Number
的数字(包括任意精度的)。 如何在 Scala 中定义一个满足数字类型对象的接口(interface)?
我目前正在用鸭子打字来破解它:
Any {
def toInt: Int
def toDouble: Double
}
这不仅冗长,而且会产生运行时反射成本。有更好的吗?
最佳答案
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)
)。为了使用这些,只需导入隐式
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
甚至可以是您的项目私有(private)的,并且这种模式仍然有效。
关于Scala 等价于 Java 的 Number,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/17999409/