tom*_*eng 17 scala class case map
如果我有Map[String,String]("url" -> "xxx", "title" -> "yyy"),有没有办法将其一般转换为case class Image(url:String, title:String)?
我可以写一个帮手:
object Image{
def fromMap(params:Map[String,String]) = Image(url=params("url"), title=params("title"))
}
Run Code Online (Sandbox Code Playgroud)
但有没有办法一次性写一次地图到任何案例类?
首先,如果您只想缩短代码,可以使用一些安全的替代方案.可以将伴侣对象视为一个函数,以便您可以使用以下内容:
def build2[A,B,C](m: Map[A,B], f: (B,B) => C)(k1: A, k2: A): Option[C] = for {
v1 <- m.get(k1)
v2 <- m.get(k2)
} yield f(v1, v2)
build2(m, Image)("url", "title")
Run Code Online (Sandbox Code Playgroud)
这将返回包含结果的选项.或者你可以ApplicativeBuilder在Scalaz中使用s,它在内部做的几乎相同,但语法更好:
import scalaz._, Scalaz._
(m.get("url") |@| m.get("title"))(Image)
Run Code Online (Sandbox Code Playgroud)
如果你真的需要通过反射来做到这一点,那么最简单的方法就是使用Paranamer(就像Lift-Framework那样).Paranamer可以通过检查字节码来恢复参数名称,因此存在性能损失,并且由于类加载器问题(例如REPL),它不能在所有环境中工作.如果您将自己限制为只包含String构造函数参数的类,那么您可以这样做:
val pn = new CachingParanamer(new BytecodeReadingParanamer)
def fill[T](m: Map[String,String])(implicit mf: ClassManifest[T]) = for {
ctor <- mf.erasure.getDeclaredConstructors.filter(m => m.getParameterTypes.forall(classOf[String]==)).headOption
parameters = pn.lookupParameterNames(ctor)
} yield ctor.newInstance(parameters.map(m): _*).asInstanceOf[T]
val img = fill[Image](m)
Run Code Online (Sandbox Code Playgroud)
(请注意,此示例可以选择默认构造函数,因为它不会检查您要执行的参数计数)
这是使用内置scala/java反射的解决方案:
def createCaseClass[T](vals : Map[String, Object])(implicit cmf : ClassManifest[T]) = {
val ctor = cmf.erasure.getConstructors().head
val args = cmf.erasure.getDeclaredFields().map( f => vals(f.getName) )
ctor.newInstance(args : _*).asInstanceOf[T]
}
Run Code Online (Sandbox Code Playgroud)
要使用它:
val image = createCaseClass[Image](Map("url" -> "xxx", "title" -> "yyy"))
Run Code Online (Sandbox Code Playgroud)