Используя Scala 2.8, вы можете написать:
case class Vector3[T: Numeric](val x: T, val y: T, val z: T) {
override def toString = "(%s, %s, %s)" format (x, y, z)
def add(that: Vector3[T]) = new Vector3(
plus(x, that.x),
plus(y, that.y),
plus(z, that.z)
)
private def plus(x: T, y: T) = implicitly[Numeric[T]] plus (x, y)
}
Позвольте мне объяснить. Во-первых, T: Numeric
является границей контекста, которая неявно предоставляет экземпляр Numeric[T]
вашему классу.
Черта Numeric[T]
обеспечивает операции над числовыми типами,
trait Numeric[T] extends Ordering[T] {
def plus(x: T, y: T): T
def minus(x: T, y: T): T
def times(x: T, y: T): T
def negate(x: T): T
// other operations omitted
}
Выражение implicitly[Numeric[T]]
извлекает этот неявный контекст, так что вы можете выполнять такие операции, как plus
, с вашими конкретными аргументами x, y и z, как показано в приватном методе выше.
Теперь вы можете создавать и add
различные экземпляры Vector3
, такие как Int
и Double
:
scala> Vector3(1,2,3) add Vector3(4,5,6)
res1: Vector3[Int] = (5, 7, 9)
scala> Vector3(1.1, 2.2, 3.3) add Vector3(4.4, 5.5, 6.6)
res2: Vector3[Double] = (5.5, 7.7, 9.899999999999999)
Примечание: возможно использовать неявные преобразования для преобразования значений в Numeric[T].Ops
экземпляры, чтобы вместо этого можно было написать следующее:
def add(that: Vector3[T]) = new Vector3(x + that.x, y + that.y, z + that.z)
Я сознательно решил не использовать эти неявные преобразования, поскольку они (могут) понести некоторое снижение производительности, создав временные объекты-оболочки. Фактическое влияние на производительность зависит от JVM (например, в какой степени он поддерживает escape-анализ, чтобы избежать фактического размещения объектов в куче). Использование привязки к контексту и implicitly
позволяет избежать потенциальных затрат ... за счет некоторого многословия.