我尝试使用Scala实现聪明的参数转换器功能之类的内容。
基本上在我的程序中我需要从属性文件中读取参数,所以显然它们都是字符串,然后我想转换我作为参数传递的特定类型中的每个参数。
这是我开始编码的实现:
def getParam[T](key : String , value : String, paramClass : T): Any = {
value match {
paramClass match {
case i if i == Int => value.trim.toInt
case b if b == Boolean => value.trim.toBoolean
case _ => value.trim
}
}
/* Exception handling is missing at the moment */
}
用法:
val convertedInt = getParam("some.int.property.key", "10", Int)
val convertedBoolean = getParam("some.boolean.property.key", "true", Boolean)
val plainString = getParam("some.string.property.key", "value",String)
注意事项:
对于我的程序,我现在需要 3主要类型:String,Int和Boolean, 如果可能,我想扩展到更多对象类型
这不聪明,因为我需要明确匹配每个可能的转换类型,我想要一个更像反思的方法
这段代码不起作用,它给我编译错误:" 对象java.lang.String不是值"当我尝试转换时(实际上没有转换,因为属性值以字符串形式出现)。
任何人都可以帮助我吗?我在Scala很新手,也许我错过了什么
答案 0 :(得分:2)
您尝试解决的问题的Scala方法是上下文边界。给定T
类型,您可以要求ParamMeta[T]
这样的对象,它会为您完成所有转换。因此,您可以将代码重写为以下内容:
trait ParamMeta[T] {
def apply(v: String): T
}
def getParam[T](key: String, value: String)(implicit meta: ParamMeta[T]): T =
meta(value.trim)
implicit case object IntMeta extends ParamMeta[Int] {
def apply(v: String): Int = v.toInt
}
// and so on
getParam[Int](/* ... */, "127") // = 127
甚至没有必要抛出异常!如果您提供不受支持的类型为getParam
类型参数,则代码甚至不会编译。您可以使用上下文边界getParam
的语法糖重写T: Bound
的签名,这将需要隐式值Bound[T]
,您需要使用implicitly[Bound[T]]
来访问该值(因为它没有参数名称)。
此代码根本不使用反射,因为编译器搜索隐式值ParamMeta[Int]
,在对象IntMeta
中创建并重写函数调用,如getParam[Int](..., "127")(IntMeta)
,因此它将获得编译时所有必需的值。
如果你觉得编写那些case object
s太过样板了,并且你确定将来在这些对象中不需要另一种方法(例如,将T
转换回{{ 1}}),您可以简化这样的声明:
String
为避免每次使用case class ParamMeta[T](f: String => T) {
def apply(s: String): T = f(s)
}
implicit val stringMeta = ParamMeta(identity)
implicit val intMeta = ParamMeta(_.toInt)
时导入它们,您可以在getParam
特征/案例类的伴随对象中声明这些含义,Scala会自动选择它们。
对于原始ParamMeta
方法,您可以将隐式match
传递给您的函数,这样您就可以匹配类。您不需要为ClassTag[T]
创建任何值,因为编译器会自动传递它。这是一个如何进行类匹配的简单示例:
ClassTag
但是,此方法的灵活性低于import scala.reflect.ClassTag
import scala.reflect._
def test[T: ClassTag] = classTag[T].runtimeClass match {
case x if x == classOf[Int] => "I'm an int!"
case x if x == classOf[String] => "I'm a string!"
}
println(test[Int])
println(test[String])
,并且ParamMeta
应该首选。