tutorial software renault examples scala

software - Equivalente de Scala del número de Java



scala vs java (2)

Estoy intentando construir una jerarquía de tipos para tipos de dominio numéricos. por ejemplo, un Year es un número Int (que es un Number ), un Percentage es un número Double , que es un Number , etc. Necesito la jerarquía para poder invocar toInt o toDouble en los valores.

Sin embargo, la jerarquía de tipos de Scala para los tipos numéricos primitivos no tiene un antecesor común, excepto AnyVal . Esto no contiene las funciones to{Int, Double} que necesito.

El tipo más cercano que pude encontrar es Numeric[T] , que parece existir principalmente para algunos trucos de compilación.

En Java, todos los números derivados de Number (incluidos los de precisión arbitraria). ¿Cómo se define una interfaz que atiende los tipos numéricos de objetos en Scala?

Actualmente estoy pirateando con pato escribiendo:

Any { def toInt: Int def toDouble: Double }

que no solo es largo, sino que también genera costos de reflexión en tiempo de ejecución. ¿Hay algo mejor?


la respuesta de @ gzm0 es una solución estática, que el tipo debe verificarse en tiempo de compilación, doy una solución dinámica que arroje el tipo en tiempo de ejecución,

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

Utiliza mayúsculas y minúsculas para elegir el tipo correcto en tiempo de ejecución.


Numeric[T] es exactamente lo que estás buscando. La forma de Scala de ir aquí son las clases de tipo (es decir, una cosa como Numeric ).

En lugar de

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

escribe uno de

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

donde el segundo es (casi) nada más que azúcar sintáctico.

Numeric.Ops

Escribir llamadas a la instancia de Numeric cada vez que necesita una operación puede volverse torpe cuando la expresión es más compleja. Para mitigar esto, Numeric proporciona la conversión implícita mkNumericOps que aumenta T con las formas comunes de escribir operaciones matemáticas (es decir, 1 + 2 lugar de n.plus(1,2) ).

Para usarlos, solo importa los miembros del Numeric implícito:

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

Tenga en cuenta que debido a las restricciones en la import la sintaxis abreviada para lo implícito es difícilmente deseable aquí.

Tipo Clases

¿Qué pasa aquí? Si una lista de argumentos está marcada como implicit , el compilador colocará automáticamente un valor del tipo requerido si hay exactamente un valor de ese tipo que está marcado como implicit en el alcance. Si tú escribes

foo(1.0)

El compilador cambiará automáticamente esto a

foo(1.0)(Numeric.DoubleIsFractional)

proporcionando el método foo con operaciones en Double .

La gran ventaja de esto es que puedes hacer tipos Numeric sin que ellos lo sepan. Supongamos que tiene una biblioteca que le da un tipo MyBigInt . Ahora supongamos que en el mundo de Java, lamentablemente, los desarrolladores no lo hicieron ampliar Number . No hay nada que puedas hacer.

En Scala, puedes escribir

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

y todo su código usando Numeric ahora funcionará con MyBigInt pero no tuvo que cambiar la biblioteca . Entonces Numeric podría incluso ser privado para su proyecto y este patrón aún funcionaría.