Scala equivalent of Java's Number

前端 未结 2 470
离开以前
离开以前 2020-11-30 07:30

I am trying to construct a type hierarchy for numerical domain types. e.g. a Year is an Int (which is a Number), a Percentage

相关标签:
2条回答
  • 2020-11-30 07:57

    the answer from @gzm0 is a static solution, which the type must be checked in compiling time, I give a dynamic solution which cast the type in runtime,

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

    It use case match to choose the correct type in runtime.

    0 讨论(0)
  • 2020-11-30 08:07

    Numeric[T] is exactly what you are looking for. Scala's way to go here is type classes (i.e. a thing like Numeric).

    Instead of

    def foo(x: java.lang.Number) = x.doubleValue
    

    write one of

    def foo[T](x: T)(implicit n: Numeric[T]) = n.toDouble(x)
    def foo[T : Numeric](x: T) = implicitly[Numeric[T]].toDouble(x)
    

    where the second is (almost) nothing but syntactic sugar.

    Numeric.Ops

    Writing calls to the instance of Numeric every time you need an operation can become clumsy when the expression is more complex. To mitigate this, Numeric provides the implicit conversion mkNumericOps which augment T with the common ways of writing mathematical operations (i.e. 1 + 2 rather than n.plus(1,2)).

    In order to use those, just import the members of the implicit Numeric:

    def foo[T](x: T)(implicit n: Numeric[T]) = {
      import n._
      x.toDouble
    }
    

    Note that due to restrictions on import the abbreviated syntax for the implicit is hardly desirable here.

    Type Classes

    What happens here? If an argument list is marked as implicit, the compiler will automatically put a value of the required type there iff exactly one value of that type that is marked as implicit exists in scope. If you write

    foo(1.0)
    

    The compiler will automatically change this to

    foo(1.0)(Numeric.DoubleIsFractional)
    

    providing the method foo with operations on Double.

    The huge advantage of this is that you can make types Numeric without them knowing. Suppose you have a library that gives you a type MyBigInt. Now suppose that in the Java world - unfortunately - the developers did not make it extend Number. There is nothing you can do.

    In Scala, you can just write

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

    and all your code using Numeric will now work with MyBigInt but you did not have to change the library. So Numeric could even be private to your project and this pattern would still work.

    0 讨论(0)
提交回复
热议问题