2016-03-07 145 views
0

我想实现像聪明参数转换器功能使用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个主要类型的类型:字符串,整数和布尔, 如果有可能,我想扩展到更多对象类型

  • 这是不聪明,因为我需要明确的对每一个不可能性类型匹配的转换我想类似的做法

  • 更reflectional此代码不能正常工作,它给我的编译错误:“对象java.lang.String中不是值”当我尝试转换(实际上没有转换发生,因为属性值以String的形式出现)。

任何人都可以帮我吗?我在斯卡拉很新手,也许我缺少一些东西

回答

2

斯卡拉方法的问题,你试图解决的是上下文边界。给定一个类型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类型参数,代码甚至不会编译。您可以使用上下文范围,T: Bound,这将需要隐藏的价值Bound[T]语法糖改写getParam签名,您将需要使用implicitly[Bound[T]]访问值(因为将有没有它的参数名称)。

另外这个代码不使用反射可言,因为一个隐含的价值ParamMeta[Int]编译器搜索,创建它的对象IntMeta并重写函数调用像getParam[Int](..., "127")(IntMeta),所以它会在编译时获取所有所需的值。

如果你觉得写这些case object s是太样板,你确信你将不再需要在这些对象的另一种方法在未来(例如,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您可以在ParamMeta特质/箱类的同伴对象声明这些implicits时间导入它们,和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应该是首选。

+0

使用'def getParam [T:ParamMeta](key:String,value:String):T =隐式地[ParamMeta [T]] .application(value.trim)'也是可能的 –

+0

这是实现这个?我的意思是,我需要每种类型的案例对象?我相信 – Giorgio

+0

里面有一些逻辑的方法如果你真的需要用侧代码来扩展这个'getParam'函数(原始的'getParam'没有改动) - 这是最典型的方法。 否则,你可以匹配像'x匹配{case i:Int => ... case s:String => ...}' – Maxim