我读了official doc,但我还是不明白它是如何运作的。例如:
class A {
type Self
}
def seqToSet[T <: A](seq: Seq[T])
(implicit cbf: CanBuildFrom[Seq[T], T#Self, Set[T]]) {}
上面的代码可以编译......但是怎么样? Scala如何知道Set
可以构建Seq
?如何才能确保将T#Self
(几乎任何类型)放入Set[T]
?
答案 0 :(得分:3)
一般不知道。您应该提供CanBuildFrom
。
对于像这样的简单案例,您可以使用breakOut
:
class A1 extends A { type Self = A1 }
seqToSet(new A1 :: Nil)(collection.breakOut)
// compiles fine
对于更复杂的案例:
case class A2(val i: Int) extends A {
type Self = Int
}
implicit val A2cbf = new CanBuildFrom[Seq[A2],A2#Self,Set[A2]] {
import scala.collection.mutable.Builder
class A2Builder extends Builder[A2#Self,Set[A2]] {
var es = List[A2]()
def +=(elem: A2#Self): this.type = { es ::= A2(elem); this }
def clear(): Unit = es = Nil
def result(): Set[A2] = es.toSet
}
def apply() = new A2Builder
def apply(from: Seq[A2]) = apply()
}
seqToSet(new A2 :: Nil)(collection.breakOut)
// compiles fine
您应提供Builder
(使用CanBuildFrom
),使其接受方法A2#Self
中的+=
并返回Set[A2]
作为结果。在代码示例中,它使用A2
创建新的elem
:A2(elem)
。
让我们用几乎相同的参数创建更有用的方法:
def seqToSet[T <: A](seq: Seq[T])(f: T => T#Self)
(implicit cbf: CanBuildFrom[Seq[T], T#Self, Set[T]]) = {
seq.map{f}: Set[T]
}
seqToSet(A2(1) :: A2(2) :: Nil){ a2 => a2.i + 1 }
// Set[A2] = Set(A2(3), A2(2))
此方法通过转换集合类型为A
提供了一些奇怪的转换。
答案 1 :(得分:2)
CanBuildFrom[A,B,C]
是隐含的,它遵循由语言规范确定的隐式解析规则。您实际上可以直接查看Where does Scala look for implicits?以查找所有详细信息。
所以答案是:
def seqToSet[T <: A](seq: Seq[T])
(implicit cbf: CanBuildFrom[Seq[T], T#Self, Set[T]]) {}
如果范围中有CanBuildFrom[Seq[T],T#Self, Set[T]]
,将编译,如果没有,则编译失败。如果有,有人为它提供了一个实现,这意味着可以进行操作(CanBuildFrom
是一个特征,所以实例是它的具体实现)
对于集合,图书馆设计师决定遵循的接近是将隐含位置放在伴随对象中:上面你看到来自2.10.3的Seq.scala
trait Seq[+A] extends PartialFunction[Int, A]
with Iterable[A]
with GenSeq[A]
with GenericTraversableTemplate[A, Seq]
with SeqLike[A, Seq[A]] {
override def companion: GenericCompanion[Seq] = Seq
override def seq: Seq[A] = this
}
/** $factoryInfo
* The current default implementation of a $Coll is a `List`.
* @define coll sequence
* @define Coll `Seq`
*/
object Seq extends SeqFactory[Seq] {
/** $genericCanBuildFromInfo */
implicit def canBuildFrom[A]: CanBuildFrom[Coll, A, Seq[A]] = ReusableCBF.asInstanceOf[GenericCanBuildFrom[A]]
def newBuilder[A]: Builder[A, Seq[A]] = immutable.Seq.newBuilder[A]
}