如何编写绑定集合类型和元素类型的通用Scala增强方法?

eje*_*eje 6 scala implicit implicit-conversion higher-kinded-types scala-collections

如果你像我一样,你偶尔想要为Scala集合或序列编写增强的方法,但是你想要绑定集合类型以及元素类型,而不仅仅是向Seq [T]进行upcast.

eje*_*eje 7

有一种方法可以做到这一点,它的工作原理如下:

object enhance {
  import scala.language.higherKinds
  import scala.language.implicitConversions
  import scala.collection.SeqLike
  import scala.collection.generic.CanBuildFrom

  implicit class Enhance[T, S[E] <: SeqLike[E, S[E]]](seq: S[T]) {
    def first3(implicit cbf: CanBuildFrom[S[T], T, S[T]]) = seq.take(3)
    def foo = seq.iterator
    def goo(implicit cbf: CanBuildFrom[Nothing, T, S[T]]) = foo.take(3).to[S]
    def moo[U](f: T => U)(implicit cbf: CanBuildFrom[S[T], U, S[U]]) = seq.map(f)
  }
}
Run Code Online (Sandbox Code Playgroud)

使用上面的类型签名模式,增强的方法知道元素类型T(例如IntString)更高级的序列类型S(例如ListVector),因此它可以准确地返回它被调用的序列类型.

许多序列方法可能需要CanBuildFrom含义,这些Enhance含义作为隐式参数添加到方法中,在上面的示例中需要它们.

以下是示例运行,显示了所需的高级集合返回类型:

scala> import enhance._
import enhance._

scala> (1 to 10).toList.first3
res0: List[Int] = List(1, 2, 3)

scala> (1 to 10).toVector.first3
res1: Vector[Int] = Vector(1, 2, 3)

scala> (1 to 10).toList.goo
res2: List[Int] = List(1, 2, 3)

scala> (1 to 10).toVector.goo
res3: Vector[Int] = Vector(1, 2, 3)

scala> (1 to 10).toList.moo(_.toDouble)
res4: List[Double] = List(1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0)

scala> (1 to 10).toVector.moo(_.toDouble)
res5: Vector[Double] = Vector(1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0)
Run Code Online (Sandbox Code Playgroud)

  • 你也可以将类定义为`隐式类Enhance [T,S [E] <:IterableLike [E,S [E]]](seq:S [T]){`然后你不需要`asInstanceOf` (4认同)
  • 如果你在新的方法实现中使用`map`或任何其他需要`CanBuildFrom`的方法,它将无法工作.结果就是"Seq".您需要将相关的隐式`CanBuildFrom`传递给`Enhance`类或方法. (3认同)