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