将String中的类型名称反映为Scala中的实际类型

时间:2015-09-04 14:11:08

标签: scala reflection types

我有以下类对象

/tmp/zone[network-id=/tmp/network[name[contains(., 'network-2')]]/id]/zone-name

要使用此功能,请致电class DefaultValue[+A](val default: A) object DefaultValue { implicit object DefaultDouble extends DefaultValue[Double](-1.0) implicit object DefaultFloat extends DefaultValue[Float](-1f) implicit object DefaultInt extends DefaultValue[Int](-1) implicit object DefaultBoolean extends DefaultValue[Boolean](false) def value[A](implicit value: DefaultValue[A]): A = value.default } DefaultValue.value[Int]

但是,当我将用户输入设置为DefaultValue.value[Double]时,我需要能够调用DefaultValue.value[Int]:用String编写的typename,以及其他类似的。

我希望在没有模式匹配的情况下这样做,因为它基本上会超过在函数中插入TypeTag的目的。 是否有更好的方法可以使用Scala Reflection?

1 个答案:

答案 0 :(得分:1)

一个问题是你无法获得良好的回报类型。 AnyDefaultValue[_]是您可以获得的最佳效果。

您可以使用反射来编写如下内容:

import scala.reflect.runtime.{currentMirror => cm, universe => ru}

def defaultByTypename(typename: String): DefaultValue[_] = {
  val defaultValueName = "Default" + typename.trim.capitalize
  val objectSymbol = 
    ru.typeOf[DefaultValue.type].member(ru.TermName(defaultValueName))
  cm.reflectModule(objectSymbol.asModule).instance.asInstanceOf[DefaultValue[_]]
}

这适用于implicit object s。如果您希望它与implicit val一起使用(例如implicit val DefaultString = new DefaultValue[String]("~")),则必须为此案例添加代码:

def defaultByTypename(typename: String): DefaultValue[_] = {
  val defaultValueName = "Default" + typename.trim.capitalize
  val objectSymbol = 
    ru.typeOf[DefaultValue.type].member(ru.TermName(defaultValueName))
  val result = if (objectSymbol.isModule) {
    cm.reflectModule(objectSymbol.asModule).instance
  } else if (objectSymbol.isTerm) {
    cm.reflect(DefaultValue).reflectField(objectSymbol.asTerm).get
  } else sys.error("Unknown typename")
  result.asInstanceOf[DefaultValue[_]]
}

但实际上,我认为,与Map签署通信并不是一个坏主意:

val typename2DefaultValue = Map[String, DefaultValue[_]](
  "Double" -> DefaultDouble,
  "Float" -> DefaultFloat,
  "Int" -> DefaultInt,
  "Boolean" -> DefaultBoolean
)

这种方法可能更快,实际上并不难维护。此外,可以不要求对象名称和用户输入之间直接对应,或者为单个DefaultValue等提供几个可能的字符串。

此外,如果您将基类声明为sealed,并仅使用object扩展它,则可以简化此Map的创建:

import scala.reflect.runtime.{currentMirror => cm, universe => ru}

sealed class DefaultValue[+A](val default: A)

object DefaultValue {
  implicit object DefaultDouble extends DefaultValue[Double](-1.0)
  implicit object DefaultFloat extends DefaultValue[Float](-1f)
  implicit object DefaultInt extends DefaultValue[Int](-1)
  implicit object DefaultBoolean extends DefaultValue[Boolean](false)

  val typename2DefaultValue: Map[String, DefaultValue[_]] = {
    val subclasses = ru.symbolOf[DefaultValue[_]].asClass.knownDirectSubclasses
    subclasses.map { subclass =>
      subclass.name.toString.stripPrefix("Default") ->
        cm.reflectModule(cm.staticModule(subclass.fullName)).instance.asInstanceOf[DefaultValue[_]]
    }.toMap
  }
}