你的方法arraysToPoints
是多余的。您可以为您的方法使用绑定在数组参数上的视图f
,并将转换添加到 的伴随对象Point
,如下所示:
object Point {
implicit def arrayToPoint[A](a: Array[A])(implicit view: A => Double): Point =
Point(a(0), a(1))
}
case class Point(x: Double, y: Double)
def f[P](coords: Array[P])(implicit view: P => Point): Unit = coords.foreach { p =>
println(p: Point)
}
f(Array(Point(1, 2), Point(2, 3)))
f(Array(Array(1.0, 2.0), Array(3.0, 4.0)))
f(Array(Array(1, 2), Array(3, 4)))
为了同时覆盖Int
和的数组Double
,我使用了绑定在该arrayToPoint
方法上的第二个视图。否则,您将需要两个单独的转换方法Array[Int]
和Array[Double]
。
您可以将这个定义读作“获取一个可以被视为类型f
的类型的元素数组”。编译器查找此类视图的一个地方是目标类型的伴随对象,因此. 这是隐式方法的好地方。P
Point
object Point
第二种可能性是使用磁铁图案。您无需使用 in 中的视图逐点转换,而是f
一次创建一个包装器对象。这有点漂亮,对于大型数组,可以最大限度地减少直接Array[Double]
参数的损失(因为您将包装器实例化一次,但不再需要调用视图函数)。arrayToPoint
每当数组元素类型A
再次可以被视为Double
. 当然,这对它自己来说是正确的Double
,但也Int
可以看作Double
是 Scala 所谓的数字扩展(例如,你可以说val x: Double = 33
整数33
被隐式地“扩展”为双精度)。
object Points {
implicit def direct(a: Array[Point]): Points =
new Points {
val peer = a
}
implicit def indirect[A](a: Array[Array[A]])(implicit view: A => Double): Points =
new Points {
lazy val peer = a.map { c => Point(c(0), c(1)) }
}
}
trait Points {
def peer: Array[Point]
}
def f(coords: Points): Unit = coords.peer.foreach(println)
这会在伴随对象中查找从参数类型到特殊磁铁类型的隐式方法Points
。我使用lazy val
非直接数组,以便在peer
未调用该方法时我们可以保存实际的转换操作。