让我们用一个真实世界的例子。一个字符串分析器类型类,其隐式实例由将创建委托给工厂的函数创建。
import scala.reflect.runtime.universe.TypeTag
object Test {
trait Parser[+T] { def parse(input: String): T }
implicit def summonParserOf[T](implicit factory: ParserFactory[T]): Parser[T] = factory.build
trait ParserFactory[T] { def build: Parser[T] }
implicit def summonFactoryOfParsersOf[T](implicit t: TypeTag[T]): ParserFactory[T] =
new ParserFactory[T] {
def build: Parser[T] = new Parser[T] {
def parse(input: String) = {
println("T = " + t.tpe) // this outputs "T = Int" if Parser is non variant, and "T = Nothing" if Parser is covariant on T. Why?
null.asInstanceOf[T]
}
}
}
def main(args: Array[String]): Unit = {
val parserOfInt = implicitly[Parser[Int]]
parserOfInt.parse("")
}
}
工厂接收的类型参数T
在Parser
为非变量时Int
,在协变时Nothing
。为什么?
编辑1:不需要工厂。替换发生在之前。因此,测试可以简化为:
package jsfacile.test
import scala.reflect.runtime.universe.TypeTag
object Probando {
trait Parser[+T] { def parse(input: String): T }
implicit def summonParserOf[T](implicit t: TypeTag[T]): Parser[T] = new Parser[T] {
def parse(input: String): T = {
println("summon parser: T = " + t.tpe) // this outputs "T = Int" if Parser is non variant, and "T = Nothing" if Parser is covariant on T. Why?
null.asInstanceOf[T]
null.asInstanceOf[T]
}
}
def main(args: Array[String]): Unit = {
val parserOfInt = implicitly[Parser[Int]]
parserOfInt.parse("")
}
}
Parser[Nothing]
可以分配给Parser[Int]
,但选择下限而不是上限的目的是什么?
编辑2:米廷@Dmytro给出的答案和下面的有用评论,翻译成我自己的话和有限的思维范围,供以后参考我自己。
阻止我理解的是错误的想法,即当隐式值提供程序是具有参数化结果类型的def
时,编译器必须从中选择一组活值。在这种情况下,我想,它只是跳过该步骤(选择具有最具体声明类型的值的步骤)。 鉴于 summoner 函数授予编译器构建任何类型的值的权力,为什么不用让他满意的值填充隐式参数呢?如果隐式参数需要可分配给类型T
的东西,则为其指定一个类型为T
的值。给它Nothing
,这是可以分配给一切的,既不好,也没有用。
当有多个召唤师提供可分配给隐式参数类型的值时,就会出现这个想法的问题。在这种情况下,决定选择哪个召唤师的唯一一致方法是推断他们产生的值的类型集,根据既定标准(例如最具体的标准)从所述集合中选择一个类型,然后选择产生它的召唤师。
Scala 规范说
如果有多个符合条件的参数与隐式参数的类型匹配,则将使用静态重载解析规则选择一个最具体的参数
https://scala-lang.org/files/archive/spec/2.11/07-implicits.html#implicit-parameters
由于您定义了诸如
implicit def summonParserOf[T](implicit t: TypeTag[T]): Parser[T] = ...
对于协变
trait Parser[+T] { ... }
当你寻找implicitly[Parser[T]]
时,所有summonParserOf[S]
(S <: T
)都是合格的候选者,所以编译器选择最具体的一个。