【问题标题】:Scala equivalent of Java's NumberScala 等价于 Java 的 Number
【发布时间】:2016-04-22 19:03:23
【问题描述】:

我正在尝试为数字域类型构建类型层次结构。例如YearInt(这是 Number),PercentageDouble,这是 Number,等等。我需要层次结构,以便我可以调用 toInttoDouble 值。

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

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

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

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

Any {
  def toInt: Int
  def toDouble: Double
}

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

【问题讨论】:

  • Year 上调用toDouble 的用例是什么?
  • @BenJames 没有......这只是说明性的。
  • 嗯,Numeric typeclass 确实是要走的路,但 Scala 仍然使用 Java 的 Number 类,所以你也可以使用它。

标签: scala


【解决方案1】:

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)

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

数字.Ops

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

为了使用这些,只需导入隐式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 甚至可以是您的项目私有的,并且这种模式仍然有效。

【讨论】:

  • @LomigMégard 建议进行编辑,以更好地解释隐含的魔法。我将编辑帖子。
【解决方案2】:

@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") }

它使用案例匹配来在运行时选择正确的类型。

【讨论】:

  • 视情况而定。就我而言,我在 spark 框架中使用 scala。 DataFrame(在 spark 中)对象具有不同数字类型的字段,我在编译时无法获取它,但大多数 spark api 需要双字段,如果没有,它会抛出异常,因此动态解决方案适用于我的应用程序。
  • 我的评论被删除了,不是我自己删除的。我会重复一遍。这个答案与惯用的 Scala 完全相反。如果您发现自己想要这样做,则说明您使用了错误的语言,或者应该花一些时间来了解编译时输入的价值。
  • @fommit 但是,它仍然会发生。我在 HTML 中动态生成一个表格,值可以是任何东西,我需要根据它是否为数字来选择一个 CSS 类。这不是语言问题。你是对的,它不应该是首选,但有时,这是唯一的方法。我们感谢反射和运行时输入。
猜你喜欢
  • 2010-11-11
  • 1970-01-01
  • 2014-08-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-01-21
  • 1970-01-01
相关资源
最近更新 更多