用scala实现转换参数功能

时间:2016-03-07 22:14:57

标签: scala reflection functional-programming

我尝试使用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很新手,也许我错过了什么

1 个答案:

答案 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应该首选。