Подтвердить что ты не робот

Scala эквивалент номера Java

Я пытаюсь построить иерархию типов для числовых типов доменов. например a Year является Int (который является Number), a Percentage является Double, который является Number и т.д. Мне нужна иерархия, чтобы я мог вызвать toInt или toDouble для значений.

Однако иерархия типов Scala для примитивных числовых типов не имеет общего предка, кроме AnyVal. Это не содержит функции to{Int, Double}, которые мне нужны.

Самый близкий тип, который я мог найти, - это Numeric[T], который, кажется, существует в основном для некоторых трюков компилятора.

В Java все числа, полученные из Number (включая произвольные точные). Как определить интерфейс, который обслуживает числовые типы объектов в Scala?

Я сейчас взламываю его с помощью утиного ввода:

Any {
  def toInt: Int
  def toDouble: Double
}

который не только длинный, но и требует затрат времени исполнения. Что-нибудь лучше?

4b9b3361

Ответ 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)

где второй (почти) ничего, кроме синтаксического сахара.

Numeric.Ops

Запись вызовов в экземпляр 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 может быть даже закрытым для вашего проекта, и этот шаблон все равно будет работать.

Ответ 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") }

Он использует совпадение для выбора правильного типа во время выполнения.