使用 Scala 实现转换参数函数



我正在尝试使用 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

最新更新