问题描述
让我们使用一个真实的例子。字符串解析器类型类,其隐式实例由将创建委托给工厂的函数创建。
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
是非变量时,工厂接收的类型参数Int
是Parser
,而当它是协变时,则是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 Mitin给出的答案和以下有用的评论,被翻译成我自己的字眼和有限的思维范围,以供日后参考。
令我难以理解的是一个错误的想法,即当隐式值提供者是具有参数化结果类型的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
)都是合格的候选人,因此编译器会选择最具体的候选人。