如何概括圆方法

yjs*_*hen 0 scala

我有以下四种方法,使用BigDecimal来舍入一个数字:

private def round(input: Byte, scale: Int): Byte = {
  BigDecimal(input).setScale(scale, RoundingMode.HALF_UP).byteValue()
}

private def round(input: Short, scale: Int): Short = {
  BigDecimal(input).setScale(scale, RoundingMode.HALF_UP).shortValue()
}

private def round(input: Int, scale: Int): Int = {
  BigDecimal(input).setScale(scale, RoundingMode.HALF_UP).intValue()
}

private def round(input: Long, scale: Int): Long = {
  BigDecimal(input).setScale(scale, RoundingMode.HALF_UP).longValue()
}
Run Code Online (Sandbox Code Playgroud)

并计划将其概括为一轮:

private def round[T](input: Any, scale: Int, f: (BigDecimal) => T): T = {
  f(BigDecimal(input.asInstanceOf[T]).setScale(scale, RoundingMode.HALF_UP))
}
Run Code Online (Sandbox Code Playgroud)

并像这样使用这一轮:

round[Byte](b, scale, _.byteValue)
round[Short](s, scale, _.shortValue)
Run Code Online (Sandbox Code Playgroud)

但上述概括round无效,因为BigDecimal.apply不能适用T,我应该做什么?

Pet*_*ens 5

您可以使用Numeric类型类

def round[T](input: T, scale: Int, f: BigDecimal => T)(implicit n: Numeric[T]): T = { 
   f(BigDecimal(n.toDouble(input)).setScale(scale, RoundingMode.HALF_UP)) 
}
Run Code Online (Sandbox Code Playgroud)

哪个可以用作:

round(5.525, 2, _.doubleValue)
res0: Double = 5.53

round(123456789L, -5, _.longValue)
res1: Long = 123500000
Run Code Online (Sandbox Code Playgroud)

另一种方法可能是创建一个BigDecimalConverter类型类,它不是简洁但解决了转换Double问题(对于泛型函数来说这不是一个好主意,如下面的RégisJean-Gilles评论).

更新了fromBigDecimal清理round功能的方法(感谢RégisJean-Gilles).

trait BigDecimalConverter[T] {
  def toBigDecimal(in: T) : BigDecimal
  def fromBigDecimal(bd: BigDecimal) : T
}

object BigDecimalConverter {
  implicit object IntToBigDecimal extends BigDecimalConverter[Int] {
    def toBigDecimal(in: Int) = BigDecimal(in)
    def fromBigDecimal(bd: BigDecimal) = bd.toInt
  }

  implicit object DoubleToBigDecimal extends BigDecimalConverter[Double] {
    def toBigDecimal(in: Double) = BigDecimal(in)
    def fromBigDecimal(bd: BigDecimal) = bd.toDouble
  }

  implicit object LongToBigDecimal extends BigDecimalConverter[Long] {
    def toBigDecimal(in: Long) = BigDecimal(in)
    def fromBigDecimal(bd: BigDecimal) = bd.toLong
  }

  implicit object BigDecimalToBigDecimal extends BigDecimalConverter[BigDecimal] {
    def toBigDecimal(in: BigDecimal) = in
    def fromBigDecimal(bd: BigDecimal) = bd
  }
}

def round[T](input: T, scale: Int)(implicit bdc: BigDecimalConverter[T]): T = 
   bdc.fromBigDecimal(
     bdc.toBigDecimal(input).setScale(scale, BigDecimal.RoundingMode.HALF_UP)
   )
Run Code Online (Sandbox Code Playgroud)

可与正确使用Double,Long,BigDecimal,...:

round(10, 1)
round(Long.MaxValue - 1000L, -1)
round(BigDecimal("1234"), -2)
Run Code Online (Sandbox Code Playgroud)

  • 然而,转换为双倍是一个合理的担忧.如果`input`本身是一个`BigDecimal`(或任何无法安全转换为'Double`)的东西怎么办?现在确实OP的代码片段没有这种情况,但即使是"Long" - 就像在OP代码中的第4次重载一样 - 它也是不安全的(并非所有`Long都适合双重). (3认同)
  • 您使用`ToBigDecimal`更新可能就是我要做的.虽然考虑到整个代码比原始代码更大,但很容易看起来毫无意义,但优点是`ToBigDecimal`类类是一个通用的粘合剂,然后可以在其他环境中随意重用.当你在它的时候,你甚至可以添加一个`fromBigDecimal`方法,允许完全删除对显式`f`参数的需要(你只需要`round(10,1)`而不是'round(10) ,1,_.toInt)`) (2认同)