我正在尝试使用 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 种主要类型:字符串、Int 和布尔值,如果可能的话,我想扩展到更多的对象类型
这并不聪明,因为我需要针对要转换的每个可能类型显式匹配,我想要一种更具反射性的方法
这段代码不起作用,当我尝试转换时,它给了我编译错误:"对象java.lang.String不是一个值"(实际上没有发生转换,因为属性值是字符串)。
谁能帮我?我是 Scala 的新手,也许我错过了一些东西
对于您尝试解决的问题,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
太样板,并且您确定将来在这些对象中不需要其他方法(例如,将T
转换回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
时都导入它们,您可以在 trait/case 类的配套对象中声明ParamMeta
这些隐式,Scala 将自动选择它们。
至于原始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
方法灵活,应首选ParamMeta
。