在Scala中确定运行时的类型参数

时间:2015-04-14 11:41:32

标签: scala type-parameter

例如,我的代码如下所示:

class Parent
class Child1 extends Parent
class Child2 extends Parent

class Foo {

  def retrieve(arg: String): List[Parent] = {
    arg match {
      case "Child1" => get[Child1]()
      case "Child2" => get[Child2]()
    }
  }

  def get[T: Manifest](): List[T] = ...

}

retrieve方法中,我想将代码简化为一个get方法调用,如下所示:

  def retrieve(arg: String): List[Parent] = {
    val t = arg match {
      case "Child1" => ?
      case "Child2" => ?
    }

    get[t]()
  }

是否有可能在scala中实现这一目标?

更新

我从这里的答案中尝试了解决方案,但我遇到了问题,它不适用于重载的get方法,例如:

def get[T: Manifest](x: String): List[T] = ...

def get[T: Manifest, U: Manifest](x: String): List[(T, U)] = ...

例如,在retrieve

val t = arg match {
  case "Child1" => manifest[Child1]
  case "Child2" => manifest[Child2]
}

get("test")(t)

我在ambiguous reference to overloaded definition行上遇到get("test")(t)编译错误。

2 个答案:

答案 0 :(得分:3)

您的问题归结为如何检索给定类型的Manifest。这可以使用manifest方法完成。然后你可以明确地将清单传递给get

class Foo {
  def retrieve(arg: String): List[Parent] = {
    val t = arg match {
      case "Child1" => manifest[Child1]
      case "Child2" => manifest[Child2]
    }

    get(t)
  }

  def get[T <: Parent: Manifest]: List[T] = ...
}

作为旁注,您应该使用地图来检索清单(而不是模式匹配),以便更容易编辑,或者可能在某一点上用一些init替换硬编码的类型列表时间计算:

object Foo {
  private val manifestByName = Map[String, Manifest[_<:Parent]](
    "Child1" -> manifest[Child1],
    "Child2" -> manifest[Child2]
  )
}
class Foo {
  def retrieve(arg: String): List[Parent] = {
    val t = Foo.manifestByName.getOrElse(arg, sys.error(s"Invalid class name $arg"))
    get(t)
  }

  def get[T <: Parent: Manifest]: List[T] = { println(manifest[T]); Nil }
}

最后请注意,Manifest现已弃用,已被ClassTag \ TypeTag取代。

答案 1 :(得分:2)

Manifest基本上是deprecated。 :

  

在Scala 2.10中,不推荐使用scala.reflect.ClassManifests,它是   计划弃用scala.reflect.Manifest以支持TypeTags和   ClassTags将在即将发布的版本中发布。因此,建议   迁移任何基于Manifest的API以使用Tags。

您应该考虑使用更现代的ClassTagTypeTag。在这种情况下,ClassTag效果更好(因为TypeTags不能用于模式匹配):

def retrieve(arg: String): List[Parent] = {
    val t = arg match {
      case "Child1" => classTag[Child1]
      case "Child2" => classTag[Child2]
    }

    get(t)
}

def get[T : ClassTag]: List[T] = list collect { 
    case x: T => x 
}

您可以在文档here中详细了解ClassTagsTypeTags及其与Manifest的关系。

如果不清楚,这是有效的,因为T上的类型约束是上下文绑定,这意味着get的方法签名等同于:

def get[T](implicit ev: ClassTag[T]): List[T]

因此,当我们调用get(t)时,我们明确指定隐式参数。阅读有关上下文边界here的更多信息。

如果上下文绑定或隐式参数混乱,您还可以通过使get非泛型来实现目标:

def get(c: ClassTag[_]) = list collect { 
    case x if ClassTag(x.getClass) == c => x 
}

这种非通用的非隐式版本可能会帮助您解决重载问题。