考虑以下REPL会话:
@ def test[C[X] <: TraversableOnce[X]](implicit cbf: CanBuildFrom[C[Int], Int, C[Int]]) = cbf()
defined function test
@ test[List]
res32: collection.mutable.Builder[Int, List[Int]] = ListBuffer()
@ def test[C[X] <: TraversableOnce[X]] = implicitly[CanBuildFrom[C[Int], Int, C[Int]]]
cmd33.sc:1: Cannot construct a collection of type C[Int] with elements of type Int based on a collection of type C[Int].
def test[C[X] <: TraversableOnce[X]] = implicitly[CanBuildFrom[C[Int], Int, C[Int]]]
^
Compilation Failed
test
函数的第一个定义编译和工作,而第二个定义不编译。它们之间的唯一区别在于如何获得CanBuildFrom
的实例。在第一种情况下,它被声明为隐式参数,需要编译器找到一个。在第二种情况下,它通过implicitly
函数调用,理论上,它在隐式搜索范围方面应该表现相同。是什么导致了这种行为?
答案 0 :(得分:1)
implicitly
(Predef
)的定义是:
def implicitly[A](implicit ev: A): A = A
它只是向您明确了隐含的范围(在使用网站)。
现在你写这个:
import collection.generic.CanBuildFrom
def test[C[X] <: TraversableOnce[X]]
(implicit cbf: CanBuildFrom[C[Int], Int, C[Int]]) = ???
您要求来电者提供隐式(在呼叫网站)。
写作时
def test[C[X] <: TraversableOnce[X]] =
implicitly[CanBuildFrom[C[Int], Int, C[Int]]]
您要求编译器调用implicitly
来查找已在范围内的隐式 。 但是你没有在范围内隐含任何给定类型!所以test
的两个定义正在做一些完全不同的事情。
您通常使用implicitly
来获取您没有名称的隐式,因为它是使用context-bounds或type-class表示法指定的,例如def test[A: TypeClass]
。您不能在此处使用该表示法,因为CanBuildFrom
有三个类型参数而不是一个。所以你在这里implicitly
做不了多少。
您可以在第一个案例中使用implicitly
:
def test[C[X] <: TraversableOnce[X]]
(implicit cbf: CanBuildFrom[C[Int], Int, C[Int]]) = {
implicit val onceAgain = implicitly[CanBuildFrom[C[Int], Int, C[Int]]]
assert(onceAgain == cbf)
}
但是你已经知道你有一个隐含的名字cbf
...
请注意,您可以获取已知集合类型的隐式CanBuildFrom
:
implicitly[CanBuildFrom[List[Int], Int, List[Int]]] // works!
但是如果您的集合类型(C[X]
)是抽象的,那么这不起作用。