Scala:'隐式'和类型参数

时间:2011-05-20 20:13:59

标签: generics scala type-inference implicit

我在理解以下现象方面遇到了一些麻烦:

trait Trait[A] {
  def traitType: String
}

object Trait {
  implicit val stringTrait: Trait[String] = new Trait[String] {
    def traitType: String = "string"
  }

  implicit val intTrait: Trait[Int] = new Trait[Int] {
    def traitType: String = "int"
  }
}

class Media[A] {
  // works
  def mediaType(implicit t: Trait[A]): String = t.traitType
  // does not compile
  def mediaType: String = implicitly[Trait[A]].traitType
}

object Main {
  def main(args: Array[String]) {
    val a = new Media[String]
    val b = new Media[Int]

    println(a.mediaType)
    println(b.mediaType)
  }
}

在上面的代码片段中,我展示了 mediaType 方法的两种不同实现(我在编译代码时将其中一个注释掉)。但是使用隐式的版本无法编译?我收到以下错误消息:

impl.scala:19: error: could not find implicit value for parameter e: Trait[A]
  def mediaType: String = implicitly[Trait[A]].traitType
                                    ^
one error found

我明白Trait [A]没有隐含价值。我不明白为什么A不能解析为Media实例化的类型。我认为我在这里考虑的是C ++模板太多了,如果有人能给我一个指向正确方向的指针,我将非常感激。

此致 raichoo

2 个答案:

答案 0 :(得分:9)

编译器需要证据,Trait存在隐式A实例。在第一个mediaType实现中,您声明了此要求。但是在第二个实现中,从编译器的角度来看,没有这样的保证。因此,为了使其工作,您应该要求Media类的用户提供它。您可以使用上下文绑定来实现此目的:

class Media[A : Trait] {
  def mediaType: String = implicitly[Trait[A]].traitType
}

这也可以更明确地写出来:

class Media[A](implicit val evidence: Trait[A]) {
  def mediaType: String = implicitly[Trait[A]].traitType
}

因此换句话说,默认构造函数需要隐式evidence,并且用户无法在不提供Media类的情况下(显式或隐式)实例化。{/ p>

答案 1 :(得分:2)

如果您想要编译此版本:

def mediaType: String = implicitly[Trait[A]].traitType

然后需要传递Trait[A]的隐式实例,例如何时创建Media的新实例。请尝试按以下方式定义Media

class Media[A](implicit private val t: Trait[A]) {
  def mediaType: String = t.traitType
}

使用context bound的几乎相同的定义就是这个:

class Media[A: Trait] {
  def mediaType: String = implicitly[Trait[A]].traitType
}

话虽如此,如果您要做的是保留有关参数化类型的类型参数的更多信息,您可能希望使用Manifests而不是您自己的机制。他们会在运行时为您提供A的完整类型信息,包括A本身是参数化类型:

scala> class Media[A](implicit val aManifest: Manifest[A])
defined class Media

scala> new Media[Int].aManifest
res0: Manifest[Int] = Int

scala> new Media[Seq[(Int, String)]].aManifest        
res1: Manifest[Seq[(Int, String)]] = scala.collection.Seq[scala.Tuple2[Int, java.lang.String]]