将 String 中的类型名称反映为 Scala 中的实际类型
Reflect type name in String to actual type in Scala
我有以下 class-object
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]
等
但是,当我有用户输入 "Int"
时,我需要能够调用 DefaultValue.value[Int]
:用字符串编写的类型名,其他人也类似。
我希望在没有模式匹配的情况下执行此操作,因为它基本上会破坏在函数中插入 TypeTag 的目的。
有没有更好的方法来做到这一点,也许是使用 Scala 反射?
一个问题是您将无法获得好的 return 类型。 Any
或 DefaultValue[_]
是您能得到的最好的。
你可以使用反射来写这样的东西:
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
秒。如果你想让它与 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
,等等
此外,如果您将基础 class 声明为 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
}
}
我有以下 class-object
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]
等
但是,当我有用户输入 "Int"
时,我需要能够调用 DefaultValue.value[Int]
:用字符串编写的类型名,其他人也类似。
我希望在没有模式匹配的情况下执行此操作,因为它基本上会破坏在函数中插入 TypeTag 的目的。 有没有更好的方法来做到这一点,也许是使用 Scala 反射?
一个问题是您将无法获得好的 return 类型。 Any
或 DefaultValue[_]
是您能得到的最好的。
你可以使用反射来写这样的东西:
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
秒。如果你想让它与 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
,等等
此外,如果您将基础 class 声明为 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
}
}